• Docs
  • APIs
Sign in
Terminal3 Payments
Integration Overview
  • Widget API
    • Digital Goods
    • Subscription-Tokidoki
      • Create Subscription
      • Change Subscription
      • Cancel Subscription
      • Custom Settings
    • Virtual Currency
    • Offerwall
  • Checkout API
    • Onetime payment
    • Subscription
  • Direct API
    • Brick Direct
      • Create form
      • Charge
      • Subscription
      • Apply 3D Secure
      • Pingback
      • Error Codes
      • Sandbox
    • Mobiamo Direct
      • Payment flows
      • Mobiamo Carriers
    • MINT Direct
  • GamePay API
  • Modules and Platforms
  • SDKs
    • GamePay SDK - iOS
    • GamePay SDK - Android
    • AppPay SDK
    • Smart TV
    • Unity
Sandbox
  • Test payment
  • Pingback tool
  • Sandbox Reporting
Guides
  • Project Review
    • Technical Review
    • Website Standards
  • SpiderPipe
    • PayPal
    • Bitcoin Coinbase
    • Alipay
    • Wechat Pay
Payment Methods
Reference
  • Client-side Callback
  • Currency codes
  • Lang codes
  • Payment system codes
  • Product Inventory Management
    • Digital Goods
    • Virtual Currency
  • Pingback
    • Custom Parameters
  • PCI DSS Compliance
  • Signature Calculation
  • Widgets
    • Widget Errors
Terminal3 E-Commerce Shop
Accepting Payments
  • Shop Page
  • Virtual Currency Widget
  • Fast Checkout
Product Delivery
  • Uploaded Keys
  • File for Download
  • Webhooks
Pingbacks
Signature
☰

1 - Project Set up

First, you need a Terminal3 account. Register here.

This integration requires your Terminal3 project credentials:

  • Project Key: Your public project identifier
  • Secret Key: Your private project key

Your payment methods are configured through your Terminal3 Dashboard. Ensure you have enabled the payment methods you want to support:

  • Brick: Credit and debit cards
  • PayAlto: Local payment methods based on user location


2 - Core SDK Integration Instruction

Step 1: Add Core SDK to the project

Install with Swift Package Manager

  • Location: https://github.com/paymentwall/ios-gamepay-sdk
  • Ensure to add both GamePaySDK & CardinalMobile products to your Target

Install as a manual framework

  • Download the xcframework files from GitHub and move them to the project folder
  • Open Project settings → choose General tab → choose Target → Drag xcframework files into Frameworks and Libraries section → choose Embed & sign
Xcode setup
  • Ensure to add both GamePaySDK & CardinalMobile frameworks

NOTE:

The GamePaySDK requires the CardinalMobile framework for handling 3D Secure 2.0 during card processing. Add the CardinalMobile framework to your target; otherwise, the build will fail.

3D Secure 2.0 (3DS2), defined by EMVCo, is the successor to 3DS1. It supports mobile and in-app payments, improves the authentication flow, and leverages additional data for stronger fraud prevention and reduced checkout drop-offs.


Step 2: Create a configuration

The payment options displayed to users are determined by the payment methods and configurations enabled in your Merchant Area. Ensure these are set up in the Merchant Area before running tests.

Basically, the SDK supports two types of payment systems:

  • Brick
  • PayAlto

Init the PaymentSheet.Configuration instance

let configuration = PaymentSheet.Configuration()

You need to establish your display name, terms of service URL, and privacy policy URL, ensuring that users can access them.

configuration.merchantDisplayName = "Demo Merchant"
configuration.merchantTermsOfServiceURL = "https://example.com/terms-of-service"
configuration.merchantPrivacyPolicyURL = "https://example.com/privacy-policy"
Privacy Policy Guide

For certain payment methods, users must complete authorization in an external browser/app. To redirect them back to your app afterward, configure the clientURL with your app’s deeplink. Check the guidance to configure a deeplink

configuration.clientUrl = "gp-demo://gamepay-redirect"

Be sure to include your keys in the configuration so that the SDK can do authorization on your behalf

configuration.setKeys(
	projectKey: "your-project-key",
	secretKey: "your-secret-key"
)

Step 3: Create a new payment

let payment = PaymentObject(
    itemID: "7f433654-9d34-41ef-b94f-8da635f510eb",
    userID: "7ed7105b-6bf0-49c6-871f-b66d11690126",
    name: "Test Payment",
    price: 10,
    currency: "USD",
    countryCode: "US",
    email: "user@example.com",
    customParams: [:]
)

You can add custom pingback parameters via customParams. Check the documentation for details


Step 4: Present PaymentSheet

Init and present a PaymentSheet

class ViewController: UIViewController {
	 var paymentSheet: PaymentSheet?
	 
	 func checkout() {
				// Setup payment methods 
        let configuration = PaymentSheet.Configuration()
				configuration.merchantDisplayName = "Demo Merchant"
				configuration.merchantTermsOfServiceURL = "https://example.com/terms-of-service"
				configuration.merchantPrivacyPolicyURL = "https://example.com/privacy-policy"
        let payment = PaymentObject(
				    itemID: "7f433654-9d34-41ef-b94f-8da635f510eb",
				    userID: "7ed7105b-6bf0-49c6-871f-b66d11690126",
				    name: "Test Payment",
				    price: 10,
				    currency: "USD",
				    countryCode: "US",
				    email: "user@example.com",
				    customParams: [:]
				)
        paymentSheet = PaymentSheet(payment: payment, configuration: configuration)
        paymentSheet?.present(from: self, delegate: self)
	 }
}


Step 5: Handle Payment Result

Implement PaymentSheetDelegate protocol to handle payment results:

extension ViewController: PaymentSheetDelegate {
    func handlePaymentResult(_ result: PaymentSheetResult) {
        let status: String
        switch result.status {
        case .completed:
            status = "SUCCESS"
				case .pending:
            status = "PENDING"
        case .canceled:
            status = "CANCELED"
        case .failed:
            var errorMesssage: String
            if let error = result.error,
               let gpError = error as? GPAPIClientError {
                errorMesssage = gpError.error
            } else {
                errorMesssage = result.error?.localizedDescription ?? ""
            }
            status = "ERROR: \(errorMesssage)"
        @unknown default:
            status = "UNKNOWN"
        }
        
        showAlert(title: "Payment Result",
                  message: status,
                  actionTitle: "Got it",
                  completeHandler: nil)
    }
    
    func handleChargeRequest(_ parameters: ChargeRequestParameters) {
	    // Submit a request to your server to handle `Brick Charge Request`
    }
}


3 - Payment Methods Integration Details

3.1 - Brick


Brick sequence diagram

Step 1: Obtain a one-time token derived from the card details.

When a user selects card payment, they can use a previously saved card for quick checkout if they’ve given consent to store cards. If not, they must manually enter their card details. The SDK will then tokenize this information to comply with global security standards for card processing.

Step 2: Create A Charge

The SDK automatically retrieves the one-time token and returns it to your application. You can then use it in your backend to initiate a charge request to our server.

Client Side: Implement handleChargeRequest **from PaymentSheetDelegate

func handleChargeRequest(_ parameters: GamePaySDK.ChargeRequestParameters) {}

Server Side:

<?php

Paymentwall_Config::getInstance()->set(array(
    'private_key' => 'YOUR_PRIVATE_KEY'
));

$parameters = $_POST;
$chargeInfo = array(
    'email' => $parameters['email'],
    'history[registration_date]' => '1489655092',
    'amount' => 9.99,
    'currency' => 'USD',
    'token' => $parameters['brick_token'],
    'fingerprint' => $parameters['brick_fingerprint'],
    'description' => 'Order #123',
    'secure' => '1',
		'secure_return_method' => "url",
		'secure_redirect_url' => $redirectUrl
);

$charge = new Paymentwall_Charge();
$charge->create($chargeInfo);

Parameters and descriptions can be found here.

Step 3: Handle Charge Response

If the charge request is successful, pass the data to the completionHandler so the SDK can process it

func handleChargeRequest(_ parameters: GamePaySDK.ChargeRequestParameters) {
    var req = URLRequest(url: URL(string: "https://your-server-domain/charge")!)
    var bodyDict: [String: Any?] = [
        "token": parameters.cardToken,
        "email": parameters.cardData["email"],
        "firstname": parameters.cardData["firstname"],
        "lastname": parameters.cardData["lastname"],
        "amount": parameters.payment.price,
        "currency": parameters.payment.currency,
    ]
    if let refID = parameters.refID {
        bodyDict["reference_id"] = refID
    }
    if let secureToken = parameters.secureToken {
        bodyDict["brick_secure_token"] = secureToken
    }
    if let chargeID = parameters.chargeId {
        bodyDict["brick_charge_id"] = chargeID
    }
    if let data = try? JSONSerialization.data(withJSONObject: bodyDict, options: []) {
        req.httpBody = data
    }
    req.httpMethod = "POST"
    req.setValue("application/json", forHTTPHeaderField: "Content-Type")
    URLSession.shared.dataTask(with: req, completionHandler: { (data, response, error) in
        DispatchQueue.main.async {
            if let data = data {
                completionHandler(data)
            }
        }
    }).resume()
}

Now, there are three scenarios:

Scenario 1: Frictionless Transaction

In this flow, the user is not required to complete a 3DS challenge. The transaction is approved instantly, and the SDK returns the payment result directly to the application.

Scenario 2: 3DS2 Challenge Flow

In this flow, the user must complete a 3DS2 challenge, which is rendered natively within the application. Once the challenge is completed, the SDK provides a secureToken and prompts the application to initiate the second charge request using delegate.handleChargeRequest. At this stage, ensure that the secureToken is included in the payload of the server-side request.

Scenario 3: Webview Challenge Flow

In this flow, the 3DS challenge is managed via a webview. After the user completes the challenge, the SDK automatically submits the second charge request to your server through the secure_redirect_url that was provided in the payload of the initial charge request. From this request’s payload, you can obtain both the brick_secure_token and the brick_charge_id, which can be used to proceed with subsequent steps in the transaction lifecycle.

<?php
$chargeInfo = array(
    ... // your original charge request parameters
);

if (isset($parameters['brick_charge_id']) AND isset($parameters['brick_secure_token'])) {
    $chargeInfo['charge_id'] = $parameters['brick_charge_id'];
    $chargeInfo['secure_token'] = $parameters['brick_secure_token'];
}
?>

Step 4: Handle payment result

Once the second charge is completed successfully, the SDK will send the payment result back to your application. You can reference the charge object using result.charge

3.2 - PayAlto


Payalto

Step 1: The user selects a PayAlto payment method

Based on the payment systems enabled by the merchant in the Merchant Area, the SDK displays a list of available payment methods. Each method includes specific instructions that the user must follow to proceed.

Step 2: The user completes the required instructions

The user is prompted to provide payment information according to the method’s requirements. This may involve entering details directly within the application or being redirected to an external browser or app for authorization.

To ensure the user is redirected back to the application after completing authorization in an external flow, set configuration.clientURL with your app’s deeplink or universal link.

Step 3: Process the payment result

After the user completes the required steps, the SDK returns the payment result to the application.

4 - Link out payment

In certain countries, it’s possible to link to an external website to process payments on iOS. For instance, this guide explains how to sell in-app credits by using GamePay Checkout, which directs users to a GamePay-hosted payment page to complete their purchase.

4.1 - Create a LinkOutPaymentHandler

LinkOutPaymentHandler is an object that manages redirection to the Terminal3 payment widget through an external browser. Configure it with the following:

  • userId: The customer ID
  • clientUrl: A deeplink that brings the user back to your app after payment.
  • externalID: ID of your product. Order reference ID could also be set here. We will communicate back to you via the pingback as goodsid parameter. The maximum length is 256.
  • widget: the widget code
  • countryCode (Optional): The length must be 2-character according to ISO 3166-1 alpha-2 code of the country. Required to be in uppercase. It overrides the location detected by IP.

For more details, refer to https://docs.paymentwall.com/apis#section-checkout-onetime

Make sure your app is set up to handle the return URL properly, using universal links or a registered URL scheme. Check the guidance to configure a deeplink

let userID = UUID().uuidString
let externalID = UUID().uuidString
let setting = LinkOutPaymentHandler.WidgetSetting(projectKey: "your-project-key",
                                                  secretKey: "your-project-secret-key",
                                                  userId: userID,
                                                  clientUrl: "https://example.com/linkout-redirect",
                                                  amount: 5,
                                                  currencyCode: "USD",
                                                  externalID: externalID,
                                                  widget: "pw25",
                                                  countryCode: "US")
self.linkOutPaymentHandler = .init(with: setting, delegate: self)

4.2 - Start a checkout

Opens the checkout URL in Safari

self.linkOutPaymentHandler?.start(from: self)

4.3 - Start polling to check the payment status

After redirecting the user to the external flow, the SDK will do the polling to periodically get the payment status

4.4 - Show payment results to your user

Implement the PaymentDelegate protocol to handle payment results once the transaction is complete

extension LinkoutPaymentExampleViewController: PaymentDelegate {
    func handlePaymentResult(_ result: PaymentResult) {
        let status: String
        switch result.status {
        case .completed:
            status = "SUCCESS"
        case .pending:
            status = "PENDING"
        case .canceled:
            status = "CANCELED"
        case .failed:
            var errorMesssage: String
            if let error = result.error,
               let gpError = error as? GPAPIClientError {
                errorMesssage = gpError.error
            } else {
                errorMesssage = result.error?.localizedDescription ?? ""
            }
            status = "ERROR: \(errorMesssage)"
        @unknown default:
            status = "UNKNOWN"
        }
        
        showAlert(title: "Payment Result",
                  message: status,
                  actionTitle: "Got it",
                  completeHandler: nil)
    }
}

5 - Add Custom Parameters to the pingback request

You can add custom pingback parameters via payment.customParams.

In Project > Settings area, Custom Pingback Parameters is where you can add custom parameters that you would like to receive from a Pingback request.

Parameters with OWN value are always the ones that you have passed to us as custom parameters and want us to communicate back to you via pingback. In this way, you can manage your custom data based on processed transactions.

For example, in the setting, you add a new my_custom_param parameter with a value of OWN

Custom params

Then, in the SDK, you initiate the payment with customParams as follows:

let payment = PaymentObject(
    itemID: "7f433654-9d34-41ef-b94f-8da635f510eb",
    userID: "7ed7105b-6bf0-49c6-871f-b66d11690126",
    name: "Test Payment",
    price: 10,
    currency: "USD",
    countryCode: "US",
    email: "user@example.com",
    customParams: [
	    "my_custom_param": "test"
    ]
)

When a transaction completes, the pingback will be sent to your server as follows:

http://www.yourserver.com/anypath?goodsid=gold_membership&is_test=1&**my_custom_param=test**&ref=b1493096790&sign_version=2&slength=1&speriod=month&type=0&uid=pwuser&sig=ac785c67092b858c5de5b7981e81a7db

For more details, please check the Paymentwall documentation.

6 - Setup Deeplink

For certain payment methods, users must complete authorization in an external browser/app. To redirect them back to your app afterward, you can either configure the URL schemes or universal link

6.1 - Configure URL Schemes

We’ll begin with URL Schemes. They’re simple to set up — you just need to define the scheme your app will use. To do this, open Xcode, go to your project’s Settings → Info, and add a new URL scheme under the URL Types section.

URL Scheme

Make sure to replace sample scheme (gp-demo) with your own app scheme

Now you can test it by tapping the link gp-demo://test from Notes app, we can see the system detects the link and shows a pop up to ask for permission to open your app

6.2 - Configure Universal Link

Universal links are a bit more complex. Basically, we want IOS to relate a webpage URL to our app. To set up a universal link:

  1. Host an apple-app-site-association file on your domain.
  2. Add the Associated Domains entitlement to your app configuration.

Add an apple-app-site-association file to your domain.

Place a file named apple-app-site-association in the .well-known directory of your domain to specify which URLs your app can handle. In this file, include your App ID, prefixed by your Team ID, which you can locate on the Membership page of the Apple Developer Portal.

{
  "applinks": {
      "details": [
           {
             "appIDs": [ "37N222R38X.com.example.app"],
             "appID": "37N222R38X.com.example.app",
             "components": [
               {
                  "/": "/gamepay-redirect*",
                  "comment": "This matches any URL with a path that begins with /gamepay-redirect"
               }
             ]
           }
       ]
   }
}

Add the Associated Domains entitlement to your app configuration.

  1. Open the Signing & Capabilities tab for your app’s target in Xcode.
  2. Click + Capability, then choose Associated Domains.
  3. Add applinks:example.com to the Associated Domains list.

For more details, refer to Apple’s Universal Links for Developers documentation.

7 - Test the Integration

7.1 - Sandbox


Brick

Retrieve sandbox keys for Brick in the Merchant Dashboard.

Project setup up

Terminal3 provides you with a standard Visa test card to test payments:

  • 4242 4242 4242 4242
  • 4000 0000 0000 0002

You should use a valid expiration date to test.

CVV/CSC could be set to any 3 or 4-digit number to test payments normally. Using the CVV/VSC code below will result in a different outcome while performing test payments.

CVV/CSC Description
111 Error: Please ensure the CVV/CSC number is correct.
222 Error: Please contact your credit card issuing bank to check your balance.
333 Error: Please contact your credit card issuing bank to approve your payment.
555 Review: Your payment is under risk review and will be accept automatically after 2 mins
556 Review: Your payment is under risk review and will be declined automatically after 2 mis

PayAlto

Follow the instructions to set up the Test Payment Method.


Next Steps

  1. Test thoroughly with all supported payment methods
  2. Set up webhook endpoints to handle payment notifications
  3. Implement proper error handling and user feedback
  4. Configure your Terminal3 Dashboard with appropriate settings
  5. Test in the production environment before going live

For additional support and advanced features, visit the Terminal3 Developer Portal.

Questions?

Common questions are covered in the FAQ.
For integration and API questions, feel free to reach out Integration Team via integration@terminal3.com.
For business support, email us at sales@terminal3.com.
To contact sales, email sales@terminal3.com.