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:
- Setup your Apple Pay environment
- 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
- User selects the Apple Pay button
- Apple Pay UI is displayed and user confirms payment
- Apple Pay returns a payment token
- Your application sends the token to your server
- Your server requests a transaction with Synctera using the token
- Synctera’s payment gateway processes the transaction
- A response containing the transaction outcome is returned
Setup your Apple Pay environment
Apple Pay for iOS
1. Register for An Apple Merchant ID
- Log into your Apple Developer account
- 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
- 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
- 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 statusDECLINED
: The transaction could not be completed due to a specific rule (e.g. low balance or velocity control) - terminal statusCANCELED
: The transaction could not be completed due to error (e.g. upstream processing error) - terminal statusUNKNOWN
: The transaction status is indeterminate - non-terminal statusPENDING
: The transaction has been initialized - non-terminal status
Sandbox Testing
The following Apple sandbox test cards are supported for transaction testing.
Card Type | Card number | Success | Notes |
---|---|---|---|
Visa Credit | 4051 0693 0220 0121 | Y | |
Visa Credit | 4761 2297 0015 0465 | Y | |
Mastercard | 5204 2452 5046 0049 | N | Fail AVS |
Mastercard | 5204 2452 5052 2095 | N | Fail AVS |
Updated about 16 hours ago