PayFacto API - V2.0 - Digital Wallets : Apple Pay™ - Online Checkout

PayFacto API - V2.0 - Digital Wallets : Apple Pay™ - Online Checkout

PayFacto API - V2.0 - Digital Wallets : Apple Pay™ - Online Checkout

InApp 

Integrate Apple Pay into your iOS application and process payments through the PayFacto Payment API v2.0. There are two implementation methods depending on who decrypts the Apple Pay token — your server or PayFacto.

Choose an Integration Method

A

  Partner Decrypts

Your server decrypts the Apple Pay token and extracts the raw card data (DPAN, expiry, cryptogram). You then send the decrypted fields to PayFacto for processing.

✅ Full control over decryption

✅ You own the key management

⚠️ Requires ECDH + AES-256-GCM implementation

Apple Pay: use PayFacto payment processing certificate for signing

B

  PayFacto Decrypts

Your server forwards the raw encrypted Apple Pay token to PayFacto. PayFacto handles decryption on its end using the PayFacto payment processing certificate.

✅ Simpler — no decryption required

✅ Pass the token as-is

⚠️ PayFacto payment processing certificate must be used

Use PayFacto certificate during token generation

A
  Method A — Partner DecryptsYour server performs ECDH decryption
A1

  Decrypt the Apple Pay Token

⚠️

Always verify the Apple Pay token signature before decrypting. This confirms the token genuinely came from Apple and prevents forgery.

Follow these three steps on your server to produce the plaintext payment data:

1

  Verify the token signature (strongly recommended)

Use Apple's root certificates to validate the token signature chain. Confirm the token truly came from Apple Pay. This step is not strictly required to perform decryption, but is required for security compliance and to prevent replay attacks.

2

  Derive the symmetric AES key

Combine your merchant private key with the token's ephemeralPublicKey using ECDH (Elliptic Curve Diffie-Hellman) to produce a shared secret. Run that shared secret through Apple's KDF (key derivation function) using SHA-256 to produce the AES-256-GCM symmetric key.

3

  Decrypt the payment data

Use AES-256-GCM with the derived key and the IV defined in Apple's specification. The authentication tag is embedded in the data field. If authentication fails, discard the token immediately. A successful decryption produces a plaintext JSON payload.

JSON — Decrypted Apple Pay payload
{
"applicationPrimaryAccountNumber": "489537XXXXXX1234",
"applicationExpirationDate": "3010",
"currencyCode": "840",
"transactionAmount": 1999,
"deviceManufacturerIdentifier": "123456789012",
"paymentDataType": "3DSecure",
"paymentData": {
"onlinePaymentCryptogram": "Qk9xR3ZpS2p0aE1UeFJ5c0RkPQ==",
"eciIndicator": "05"
}
}

Fields to extract from the decrypted payload

Decrypted fieldMaps toNotes
applicationPrimaryAccountNumberCard number (DPAN)Pass as card.number in the charge request. This is the Device Primary Account Number, not the physical card number.
applicationExpirationDateExpiry dateFormat from Apple is YYMM. Remap to expiry.month (MM) and expiry.year (20YY) before sending to PayFacto.
paymentData.onlinePaymentCryptogramCryptogramBase64-encoded. Must be converted to HEX before sending to PayFacto. See Step A2 below.
paymentData.eciIndicatorECI indicatorPass as card.authentication.wallet.eciIndicator.
A2

  Remap the Expiry Date Format

ℹ️

Apple Pay returns the expiry date in YYMM format (e.g. 3010 = October 2030). PayFacto expects expiry.month (MM) and expiry.year (YYYY) as separate fields. Split and reformat before building the charge request.

Apple Pay fieldApple formatPayFacto fieldPayFacto format
applicationExpirationDate3010 → YYMMcard.expiry.month10 (MM)
applicationExpirationDate3010 → YYMMcard.expiry.year2030 (YYYY — prepend 20)
A3

  Convert the Cryptogram from Base64 to HEX

⚠️

The cryptogram returned by Apple Pay is Base64-encoded. PayFacto requires it in hexadecimal (HEX) format. Convert it before building the charge request.

EncodingExample value
Base64 (from Apple Pay)Qk9xR3ZpS2p0aE1UeFJ5c0RkPQ==
HEX (send to PayFacto)424F714776694B6A74684D547852797344643D
Java — Base64 → HEX conversion
import java.util.HexFormat;

public static String getCryptogramHexString(final ApplePayPaymentPlainData plainData) {
final String base64String = plainData.getPaymentData().getOnlinePaymentCryptogram();
final byte[] bytes = decodeBase64(base64String);
return HexFormat.of().formatHex(bytes);
}
A4

  Build and Submit the Charge

ℹ️

Use paymentMethod.type: "CARD" with a card.authentication.wallet block. Set wallet.type to APPLE_PAY and pass the HEX-encoded cryptogram.

Charge request body — Method A

FieldConstraintDescription
amountinteger · requiredAmount in cents. Example: 5000 = $50.00
invoiceNumberstring · requiredMerchant invoice reference. Max 25 chars.
captureboolean · optionalDefault: true. Set false for pre-authorization.
paymentMethod.typestring · requiredCARD
paymentMethod.card.numberstring · requiredDPAN extracted from the decrypted Apple Pay payload (applicationPrimaryAccountNumber).
paymentMethod.card.expiry.monthstring · requiredExpiry month (MM) remapped from Apple's YYMM format.
paymentMethod.card.expiry.yearstring · requiredExpiry year (YYYY) remapped from Apple's YYMM format.
paymentMethod.card.authentication.wallet.typestring · requiredAPPLE_PAY
paymentMethod.card.authentication.wallet.eciIndicatorstring · requiredECI indicator from the decrypted payload (paymentData.eciIndicator).
paymentMethod.card.authentication.wallet.cryptogramstring · requiredCryptogram converted to HEX (from paymentData.onlinePaymentCryptogram). Do not send raw Base64.
JSON — POST /charges body (Method A — Partner Decrypts)
{
"amount": "5000",
"invoiceNumber": "123456789012",
"capture": true,
"paymentMethod": {
"type": "CARD",
"card": {
"number": "489537XXXXXX1234",
"expiry": {
"month": "10",
"year": "2030"
},
"authentication": {
"wallet": {
"eciIndicator": "05",
"type": "APPLE_PAY",
"cryptogram": "424F714776694B6A74684D547852797344643D"
}
}
}
}
}
B
  Method B — PayFacto DecryptsPayFacto certificate used during token generation
B1

  Use the PayFacto Payment Processing Certificate

ℹ️

During Apple Pay token generation on the device, the PayFacto payment processing certificate must be used as the encryption certificate. This allows PayFacto to decrypt the token on its end. Contact PayFacto to obtain the certificate.

🔒

  PayFacto Payment Processing Certificate

This certificate replaces your own merchant certificate for Apple Pay token encryption. The token generated by Apple Pay is encrypted with PayFacto's public key, so only PayFacto can decrypt it. Contact your PayFacto integration team to obtain and configure this certificate in your Apple Pay setup.

B2

  Receive and Forward the Token

ℹ️

With the PayFacto certificate in place, your server receives the encrypted Apple Pay token from the device. Pass the entire token JSON string exactly as received as the paymentData field. No decryption, field extraction, or cryptogram conversion is needed.

⚠️

Pass the token value exactly as received — as a JSON string. Do not parse, modify, or re-serialize it. PayFacto will handle all decryption internally.

B3

  Build and Submit the Charge

ℹ️

Use paymentMethod.type: "CARD" with a card.authentication.wallet block. Set wallet.type to APPLE_PAY and pass the raw token JSON string as wallet.paymentData. No card number, expiry, ECI, or cryptogram fields are required.

Charge request body — Method B

FieldConstraintDescription
amountinteger · requiredAmount in cents.
invoiceNumberstring · requiredMerchant invoice reference. Max 25 chars.
captureboolean · optionalDefault: true. Set false for pre-authorization.
paymentMethod.typestring · requiredCARD
paymentMethod.card.authentication.wallet.typestring · requiredAPPLE_PAY
paymentMethod.card.authentication.wallet.paymentDatastring · requiredThe complete Apple Pay token JSON string as received from the device. Pass as-is — do not parse or modify.
JSON — POST /charges body (Method B — PayFacto Decrypts)
  "paymentMethod": {
"type": "CARD",
"card": {
"authentication": {
"wallet": {
"type": "APPLE_PAY",
"paymentData": "{\"data\":\"19T\/dTGGl3kxsA5vYwratId7Six3l...\",...}"
}
}
}
}