Instant Account Funding - Apple Pay

Overview

In addition to Instant Account Funding (PULL from card) with an external card-on-file or with Google Pay, Synctera offers Instant Account Funding with Apple Pay using an external card that the user has added to their Apple Wallet. Through this method, transactions are are securely processed with Apple Pay’s encryption and authentication features, such as Face ID or Touch ID.

📘

Synctera currently only supports Apple Pay on iOS. Web based payments are planned for a future release.

Prerequisites

To perform Instant Account Funding with Apple Pay, you must first:

  • Create a Customer
  • Create an Account for the customer
  • Have External Cards enabled with the help of your Synctera implementation representative
  • Perform the necessary steps to enable payments through Apple Pay:
    1. Setup your Apple Pay environment
    2. Integrate with Apple Pay to generate payment token:
      • Present the Apple Pay button in your app
      • Present the payment sheet to the customer
      • Receive payment token

Payment Flow

  1. User selects the Apple Pay button
  2. Apple Pay UI is displayed and user confirms payment
  3. Apple Pay returns a payment token
  4. Your application sends the token to your server
  5. Your server requests a transaction with Synctera using the token
  6. Synctera’s payment gateway processes the transaction
  7. A response containing the transaction outcome is returned

Setup your Apple Pay environment

Apple Pay for iOS

1. Register for An Apple Merchant ID

  1. Log into your Apple Developer account
  2. Follow the Apple Developer Account instructions to create a merchant identifier. We recommend including the Synctera name and environment as a prefix when creating your identifier. For example merchant.com.synctera-sandbox.mycompany

2. Configure Your App for Apple Pay

Follow the Apple Developer Account instructions to enable apple pay.

3. Create a Payment Processing Certificate

  1. Call Create an Apple Pay CSR to create a CSR.
curl \ 
 -X POST \
 $baseurl/v1/certificates/applepay/csr \ 
 -H "Authorization: Bearer $apiKey" \
 --json '
 {
	 "merchant_id": "merchant.com.synctera-sandbox.mycompany",
	 "organization_name": "My Company Inc"
 }
 ' -o ApplePay.csr
  1. Follow the Apple Developer Account instructions to create a payment processing certificate. When prompted to choose a file, select the CSR you created.

4. Renewing your Payment Processing Certificate

Apple Pay requires you to renew your Payment Processing Certificate every 25 months. In order to renew your certificate, create a new Apple Pay CSR and Payment Processing certificate. Apple requires up to 4 hours to start using your new Payment Processing Certificate, during which time transactions will still continue to process successfully with your old Payment Processing Certificate.

Integrate with Apple Pay to generate payment token

iOS

Start a Payment

    private func startPayment() {
        let paymentNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex, .discover]
        
        guard PKPaymentAuthorizationController.canMakePayments(),
              PKPaymentAuthorizationController.canMakePayments(usingNetworks: paymentNetworks) else {
            statusMessage = "Apple Pay is not available"
            statusColor = .red
            responseText = "Apple Pay is not available on this device"
            isCopyButtonEnabled = false
            return
        }
        
        // Create payment request
        let request = PKPaymentRequest()
        request.merchantIdentifier = merchantIdentifier
        request.supportedNetworks = paymentNetworks
        request.merchantCapabilities = [.capability3DS, .capabilityCredit, .capabilityDebit]
        request.countryCode = "US"
        request.currencyCode = "USD"
        
        // Configure for shipping and billing contact
        request.requiredShippingContactFields = [.postalAddress, .emailAddress, .phoneNumber, .name]
        request.requiredBillingContactFields = [.postalAddress, .name]
        
        // Add payment summary items
        let amount = NSDecimalNumber(decimal: paymentAmount)
        let subtotal = PKPaymentSummaryItem(label: "Subtotal", amount: amount)
        
        // Add tax (for demonstration)
        let taxAmount = NSDecimalNumber(decimal: paymentAmount * 0.08) // 8% tax
        let tax = PKPaymentSummaryItem(label: "Tax", amount: taxAmount)
        
        // Total amount
        let totalAmount = amount.adding(taxAmount)
        let total = PKPaymentSummaryItem(label: "Your Company Name", amount: totalAmount)
        
        request.paymentSummaryItems = [subtotal, tax, total]
        
        // Present Apple Pay sheet
        let controller = PKPaymentAuthorizationController(paymentRequest: request)
        controller.delegate = PaymentHandler.shared
        
        PaymentHandler.shared.completionHandler = { success, response in
            if success {
                if let jsonResponse = response {
                    // Log to console
                    print("=== APPLE PAY RESPONSE ===")
                    print(jsonResponse)
                    print("=========================")
                    
                    DispatchQueue.main.async {
                        // Update status message
                        self.statusMessage = "Payment Authorized Successfully"
                        self.statusColor = .green
                        
                        // Show only the raw JSON in the text view
                        self.responseText = jsonResponse
                        
                        // Enable copy button
                        self.isCopyButtonEnabled = true
                    }
                }
            } else {
                DispatchQueue.main.async {
                    self.statusMessage = "Payment failed or was cancelled"
                    self.statusColor = .red
                    self.responseText = "Payment failed or was cancelled"
                    self.isCopyButtonEnabled = false
                }
            }
        }
        
        controller.present { presented in
            if !presented {
                self.statusMessage = "Failed to present Apple Pay"
                self.statusColor = .red
                self.responseText = "Failed to present Apple Pay authorization controller"
                self.isCopyButtonEnabled = false
            }
        }
    }

Handle a Payment

// Payment Handler to manage Apple Pay delegate methods
class PaymentHandler: NSObject, PKPaymentAuthorizationControllerDelegate {
    static let shared = PaymentHandler()
    
    var completionHandler: ((Bool, String?) -> Void)?
    private var paymentSucceeded = false
    
    func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, 
                                        didAuthorizePayment payment: PKPayment, 
                                        handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
        // Process the payment by sending the token to your payment processor
        processPayment(payment) { (success, error) in
            if success {
                // Payment was processed successfully
                let paymentInfo = self.createPaymentInfoJSON(from: payment)
                completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
                self.paymentSucceeded = true
                self.completionHandler?(true, paymentInfo)
            } else {
                // Payment processing failed
                let errors = [error].compactMap { $0 }.map { NSError(domain: "PaymentError", code: 0, userInfo: [NSLocalizedDescriptionKey: $0]) }
                completion(PKPaymentAuthorizationResult(status: .failure, errors: errors))
                self.paymentSucceeded = false
                self.completionHandler?(false, error)
            }
        }
    }
    
    private func processPayment(_ payment: PKPayment, completion: @escaping (Bool, String?) -> Void) {
          // TODO - process payment with Synctera
    }

Use the Apple Pay payment token to initiate payment

Server

Now that you have generated an Apple Pay payment token through your app, your server may request an instant PULL payment using the token. Synctera’s payment gateway will then process the payment, and return the outcome to your app.

Call Create Apple Pay External Card Transfer

Example request:

curl \
	-X  POST \
	$baseurl/v1/external_cards/transfers/applepay \
	-H "Authorization: Bearer $apiKey" \
	--json '
	{
  "apple_pay_payment": {
    "token": {
      "payment_data": {
        "data": "1+aL4iT83Z7fLVZ71En6L+D1oMVNMYMBoiHPGA1Ex87WZx5ULbh/pqAVxZZCp2ePVazFRkVju8I0j73lW+lb1NINm5ZMt/WncA+0GlN4B3Wvc4YLsO4TzelEAie4OjsL0VNsTR6C383LGA2c5LQATXbqeg4Llq2wsaWMp5hDcuc8NsJ5jCLvyFDHkAFJtHdZ3k0w8s2JdY71ezgmwDnRiXSipcYCp98KRZ0GDV4T2R8NCk2nZEXixy18sy7j5mKfXSJMhcLhNBJsD8/vIugzEX7FINRidtxS9A3vyuLYeNu6rmaEL2P7XfXEa8yf0XNjWwjB8x3M2vh+JX1+ZyGFzHLHoCB8N3mpsWCeSGwNC7+y/ND0uNNN/ugprpb8cubjF5kDeJETIEw1v0XsE0180bkc8lcwO9KecczsibEXu3A=",
        "signature": "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",
        "header": {
          "public_key_hash": "dwXh9g8mdIhEFW8Fou4AsxiZK2weLfWX+ejRw1wNNbw=",
          "ephemeral_public_key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh6XRGF+UAjnlpVjeyU0EAUuZKDLPYehmokRjK4umV2zFp+w2l5rI7TnXk9x02i7NYQwVvk2M4Pj39vBjyWamOw==",
          "transaction_id": "90fe8430e36fb9b50d1f9aaacdd2a0227fc31e03a5342d34f460bd56b94cf50d"
        },
        "version": "EC_v1"
      },
      "payment_method": {
        "network": "Visa",
        "display_name": "Visa 0121",
        "type": "credit"
      },
      "transaction_identifier": "90fe8430e36fb9b50d1f9aaacdd2a0227fc31e03a5342d34f460bd56b94cf50d"
    }
  },
  "originating_account_id": "0195a5cf-e6b7-7827-bf59-b613dc55e6d3",
  "customer_id": "0195a5d0-4bed-77a3-a279-48221ca200da",
  "amount": 1080
}
'

Example response:

{
  "account_id": "0195a5cf-e6b7-7827-bf59-b613dc55e6d3",
  "amount": 1080,
  "creation_time": "2025-03-14T21:26:02.83391Z",
  "currency": "USD",
  "customer_id": "0195a5d0-4bed-77a3-a279-48221ca200da",
  "id": "0195a5d0-834d-7ea1-b85f-cf20a13fd54e",
  "last_updated_time": "2025-03-14T21:26:04.614801Z",
  "merchant": {
    "address": {
      "address_line_1": "47 Simpson Avenue",
      "city": "Shippensberg",
      "country_code": "US",
      "postal_code": "17257",
      "state": "PA"
    },
    "email": "[email protected]",
    "name": "FunTech",
    "phone_number": "+18013570346"
  },
  "status": "SUCCEEDED",
  "tenant": "kepkep_pgnwwy",
  "transaction_id": "0195a5d1-63fe-76f9-81a9-71374e1d30e3",
  "card_details": {
    "address_verification_result": "VERIFIED",
    "cvv2_result": "NOT_SUPPORTED",
    "name_verification_result": "NOT_VERIFIED",
    "pull_details": {
      "network": "Visa"
    },
    "pull_enabled": true,
    "push_enabled": false,
    "bin": "481852",
    "issuer": "",
    "last_four": "6602",
    "payment_account_reference": "V0010013022073812195104906324"
  },
  "type": "APPLE_PAY_PULL"
}

The status field of the response indicates the outcome of the transaction:

  • SUCCEEDED: The transaction was successful and funds are available - terminal status
  • DECLINED: The transaction could not be completed due to a specific rule (e.g. low balance or velocity control) - terminal status
  • CANCELED: The transaction could not be completed due to error (e.g. upstream processing error) - terminal status
  • UNKNOWN: The transaction status is indeterminate - non-terminal status
  • PENDING: The transaction has been initialized - non-terminal status

Sandbox Testing

The following Apple sandbox test cards are supported for transaction testing.

Card TypeCard numberSuccessNotes
Visa Credit4051 0693 0220 0121Y
Visa Credit4761 2297 0015 0465Y
Mastercard5204 2452 5046 0049NFail AVS
Mastercard5204 2452 5052 2095NFail AVS