• 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

Install with Cocoapods

pod 'GamePaySDK', :git => 'https://github.com/paymentwall/ios-gamepay-sdk', :tag => '<sdk_version>'

Install as a manual framework

  • Download the xcframework file from GitHub and move it to the project folder
  • Open Project settings → choose General tab → choose Target → Drag xcframework file into Frameworks and Libraries section → choose Embed & sign
Xcode setup

Step 2: Add payment Methods

You can choose one or more payment methods according to your needs.

  • Brick
  • PayAlto

Init the payment method instances and PaymentSheet.Configuration instance

let paymentOptions: [PaymentOption] = [brickOption, payAltoOption]
let configuration = PaymentSheet.Configuration(paymentOptions: paymentOptions)

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


Step 3: Create a new payment

let payment = PaymentObject(
    itemID: "testuid",
    userID: "testuid",
    name: "Test",
    price: 0.99,
    currency: "USD",
    image: nil
)

Step 4: Present PaymentSheet

Init PaymentSheet and present

class ViewController: UIViewController {
    var paymentSheet: PaymentSheet?
    
    func checkout() {
        // Setup payment methods 
        let paymentOptions: [PaymentOption] = [brickOption, payAltoOption]
        let configuration = PaymentSheet.Configuration(paymentOptions: paymentOptions)
        configuration.merchantDisplayName = "Demo Merchant"
        configuration.environment = .production
        let payment = PaymentObject(
            itemID: "testuid",
            userID: "testuid",
            name: "Test",
            price: 0.99,
            currency: "USD",
            image: nil
        )
        paymentSheet = PaymentSheet(payment: payment, configuration: configuration)
        paymentSheet?.present(from: self, delegate: self)
    }
}

You can define global keys for all options as follows:

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

Step 5: Handle Payment Result

Implement PaymentSheetDelegate protocol to handle payment result:

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)
    }
}


3 - Payment Methods Integration Details

3.1 - Brick


Brick sequence diagram

Brick sequence diagram

Step 1: Create BrickPaymentOption

let brickOption = BrickPaymentOption()
brickOption.setKeys(projectKey: {BRICK_PUBLIC_KEY},
                    secretKey: {BRICK_SECRET_KEY})

If a key is set for an individual option, it will override the global key

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(token: BrickOTTResponse, payment: PaymentObject, completionHandler: @escaping (Data) -> Void) {}

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 description 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(token: BrickOTTResponse, payment: PaymentObject, completionHandler: @escaping (Data) -> Void) {
    var req = URLRequest(url: URL(string: "https://your-server-domain/charge")!)
    let bodyDict: [String: Any] = ["token": token.token,
                                   "email": token.email]
    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()
}

If 3D Secure is necessary, the SDK will automatically show the 3D Secure page for the user to complete the challenge.

Displaying a 3D secure form for your customers is the first step. After that, brick_secure_token and brick_charge_id will be sent to secure_redirect_url via POST each time a payer confirms the 3D secure payment step. You can now continue with the next step

<?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


Step 1: Create PayAltoPaymentOption

let payAltoOption = PayAltoPaymentOption()
var payAltoSetting = PayAltoPaymentOption.Setting()
payAltoOption.setting = payAltoSetting
payAltoOption.setKeys(projectKey: {PAYALTO_PUBLIC_KEY},
                      secretKey: {PAYALTO_SECRET_KEY})

Step 2: Set up custom params. (Optional)

Set up custom params through PayAltoPaymentOption.Setting

var payAltoSetting = PayAltoPaymentOption.Setting()
payAltoSetting.successUrl = {SUCCESS_URL}
payAltoSetting.clientUrl = {CLIENT_URL}
payAltoSetting.countryCode = {USER_COUNTRY_CODE}
payAltoSetting.widget = {WIDGET_TYPE}
  • success_url: URL of the page where the end-user should be redirected to after the payment is complete. Additionally, $ref can be added as a placeholder in success_url to pass the transaction id. E.g. https://website.com/thank-you?transaction_id=$ref
  • client_url: Optional URL of pingback listener script where pingbacks should be sent. It overrides the default Pingback URL which is set up in Project Settings in Merchant Area. Please send a request to devsupport@paymentwall.com to activate this feature.
  • country_code: 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.
  • widget: Widget code. It can be obtained in the widget sections of your projects.

3.3 - Full Integration


class ViewController: UIViewController {
    var paymentSheet: PaymentSheet?

    func checkout() {
        let brickOption = BrickPaymentOption()
        brickOption.setKeys(
            projectKey: { BRICK_PUBLIC_KEY },
            secretKey: { BRICK_SECRET_KEY })

        let payAltoOption = PayAltoPaymentOption()
        var payAltoSetting = PayAltoPaymentOption.Setting()
        payAltoOption.setting = payAltoSetting
        payAltoOption.setKeys(
            projectKey: { PAYALTO_PUBLIC_KEY },
            secretKey: { PAYALTO_SECRET_KEY })

        let paymentOptions: [PaymentOption] = [brickOption, payAltoOption]
        let configuration = PaymentSheet.Configuration(paymentOptions: paymentOptions)
        configuration.merchantDisplayName = "Demo Merchant"
        configuration.environment = .production
        let payment = PaymentObject(
            itemID: "testuid",
            userID: "testuid",
            name: "Test",
            price: 0.99,
            currency: "USD",
            image: nil
        )
        paymentSheet = PaymentSheet(payment: payment, configuration: configuration)
        paymentSheet?.present(from: self, delegate: self)
    }
}


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 for completing their purchase.

4.1 - Setup deeplink

Universal links enable Checkout to open your app directly. 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": [
               {
                  "/": "/linkout-redirect*",
                  "comment": "This matches any URL with a path that begins with /linkout-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.

4.2 - 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
  • successUrl: A universal link or custom scheme 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.
  • 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 API Reference

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

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

4.3 - Start a checkout

Opens the checkout URL in Safari

self.linkOutPaymentHandler?.start { [weak self] in
    self?.showSuccessAlert()
}
doPollingToGetPaymentStatus()

Resume the workflow once the payment is completed and the user is redirected back to the app

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else { return }
    
    if URLCallBackCoordinator.handleReturnURL(url.absoluteString) {
        return
    }
}

4.4 - Start polling to check the payment status

Since transactions can occasionally take some time to complete and users might return to your application on their own, you can implement a polling request to regularly check the payment status and promptly show the result once it’s available.

private func doPollingToGetPaymentStatus() {
    linkOutPaymentHandler?.getPaymentStatus { [weak self] payments in
        guard let self else { return }
        
        if payments.isEmpty { // keep polling
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.doPollingToGetPaymentStatus()
            }
        } else { // show success
            showSuccessAlert()
        }
    }
}

4.5 - Show payment result to your user

Once the transaction is complete, you can show the result to the user

private func showSuccessAlert() {
    guard !didHandlePaymentResult else { return }
    setButtonState(true)
    showAlert(title: "Transaction Result", message: "Success", completeHandler: nil)
    didHandlePaymentResult = true
}


5 - Test the Integration

5.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.