{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","admonition","endpoint","br","details","required","code-group"]},"type":"markdown"},"seo":{"title":"Recurring payments","description":"Transform your commerce with PXP's unified platform—seamless payments, real-time insights, and global growth in one powerful integration.","lang":"en-UK","siteUrl":"https://developer.pxp.io","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"recurring-payments","__idx":0},"children":["Recurring payments"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set up recurring payments using card components on iOS."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card components support two types of recurring payments:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Merchant-initiated transactions (MITs):"]}," For subscription-based payments where the customer signs up and pays initially, and you then automatically charge them at a specified interval using your own scheduling system."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Card-on-file transactions with shopper consent:"]}," For storing payment methods with customer consent for future use, allowing faster checkout for returning customers."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["PXP doesn't provide an automatic payment scheduler. You must implement your own scheduling system to initiate subsequent recurring charges via the Transactions API."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"merchant-initiated-recurring-payments","__idx":2},"children":["Merchant-initiated recurring payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Merchant-initiated recurring payments require two steps: an initial setup transaction using the SDK, followed by subsequent charges initiated from your backend using the Transactions API."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-set-up-the-initial-recurring-payment","__idx":3},"children":["Step 1: Set up the initial recurring payment"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the card components to collect the customer's card details and set up the recurring payment. Include the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recurring"]}," object in your transaction data to specify the payment frequency."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you include the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recurring"]}," object in your transaction data, the SDK automatically sets the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["processingModel"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedInitialRecurring"]}," when creating the transaction request."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import PXPCheckoutSDK\n\n// Configure initial subscription transaction\nlet expirationFormatter = ISO8601DateFormatter()\nexpirationFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]\n\nlet transactionData = TransactionData(\n    amount: Decimal(string: \"9.99\")!,\n    currency: \"USD\",\n    entryType: .ecom,\n    intent: TransactionIntentData(card: .authorisation),\n    merchantTransactionId: \"sub-setup-123\",\n    merchantTransactionDate: { Date() },\n    recurring: RecurringType(\n        frequencyInDays: 30,\n        frequencyExpiration: expirationFormatter.string(\n            from: Calendar.current.date(byAdding: .year, value: 1, to: Date()) ?? Date()\n        )\n    )\n)\n\n// Create SDK configuration\nlet checkoutConfig = CheckoutConfig(\n    environment: .test,\n    session: sessionData,\n    transactionData: transactionData,\n    merchantShopperId: \"customer-123\",\n    ownerType: \"MerchantGroup\",\n    ownerId: \"your-owner-id\",\n    onGetShopper: { async in\n        TransactionShopper(\n            email: \"customer@example.com\",\n            firstName: \"John\",\n            lastName: \"Doe\"\n        )\n    }\n)\n\nlet pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)\n\n// Create card components\nlet cardNumberConfig = CardNumberComponentConfig(label: \"Card number\")\nlet cardNumberComponent = try pxpCheckout.create(.cardNumber, componentConfig: cardNumberConfig) as! CardNumberComponent\n\nlet expiryConfig = CardExpiryDateComponentConfig(label: \"Expiry date\")\nlet expiryComponent = try pxpCheckout.create(.cardExpiryDate, componentConfig: expiryConfig) as! CardExpiryDateComponent\n\nlet cvcConfig = CardCvcComponentConfig(label: \"CVC\")\nlet cvcComponent = try pxpCheckout.create(.cardCvc, componentConfig: cvcConfig) as! CardCvcComponent\n\n// Create card submit component\nvar submitConfig = CardSubmitComponentConfig()\nsubmitConfig.cardNumberComponent = cardNumberComponent\nsubmitConfig.cardExpiryDateComponent = expiryComponent\nsubmitConfig.cardCvcComponent = cvcComponent\nsubmitConfig.useCardOnFile = false\nsubmitConfig.submitText = \"Start subscription\"\n\nsubmitConfig.onPreAuthorisation = { _ async in\n    return TransactionInitiationData()\n}\n\nsubmitConfig.onPostAuthorisation = { result in\n    // Check result type - MerchantSubmitResult contains transaction details\n    if let merchantResult = result as? MerchantSubmitResult {\n        print(\"Subscription setup completed\")\n        print(\"System transaction ID: \\(merchantResult.systemTransactionId)\")\n        \n        // Store the system transaction ID for subsequent charges\n        Task {\n            await saveSubscription(\n                systemTransactionId: merchantResult.systemTransactionId,\n                customerId: \"customer-123\",\n                amount: Decimal(string: \"9.99\")!,\n                currency: \"USD\",\n                frequency: 30,\n                nextChargeDate: calculateNextChargeDate(days: 30)\n            )\n        }\n    } else if let failed = result as? FailedSubmitResult {\n        print(\"Transaction failed: \\(failed.errorReason ?? \"Unknown error\")\")\n    }\n}\n\nlet submitComponent = try pxpCheckout.create(.cardSubmit, componentConfig: submitConfig) as! CardSubmitComponent\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-charge-subsequent-recurring-payments","__idx":4},"children":["Step 2: Charge subsequent recurring payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For subsequent recurring charges, you must initiate transactions from your backend using the Transactions API. PXP doesn't automatically charge customers based on the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["frequencyInDays"]},", so you need to implement your own scheduler to trigger these charges."]},{"$$mdtype":"Tag","name":"Endpoint","attributes":{"method":"POST"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["/v1/transactions"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the following request to charge a customer for a subsequent recurring payment. Replace the full card details with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["gatewayTokenId"]}," you received from the initial transaction."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"merchant\": \"MERCHANT-1\",\n  \"site\": \"SITE-1\",\n  \"merchantTransactionId\": \"sub-charge-456\",\n  \"merchantTransactionDate\": \"2025-02-27T08:51:02.826Z\",\n  \"transactionMethod\": {\n    \"intent\": \"Purchase\",\n    \"entryType\": \"Ecom\",\n    \"fundingType\": \"Card\"\n  },\n  \"fundingData\": {\n    \"card\": {\n      \"gatewayTokenId\": \"5fbd77ce-02c1-40ed-94bc-1016660b7512\"\n    }\n  },\n  \"amounts\": {\n    \"transaction\": 9.99,\n    \"currencyCode\": \"USD\"\n  },\n  \"recurring\": {\n    \"processingModel\": \"MerchantInitiatedSubsequentRecurring\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Parameter"},"children":["Parameter"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchant"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (≤ 20 characters)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your unique merchant identifier, as assigned by PXP."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["site"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (≤ 20 characters)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your unique site identifier, as assigned by PXP."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionId"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (≤ 50 characters)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A unique identifier for this transaction."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionDate"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["date-time"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The date and time when the transaction happened, in ISO 8601 format."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionMethod"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Details about the transaction method."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionMethod.intent"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (enum)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payment intent. For recurring charges, use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Purchase"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorisation"]},".",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},"Possible values:",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorisation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Purchase"]}]}]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionMethod.entryType"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The entry type. For e-commerce transactions, this is always ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Ecom"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionMethod.fundingType"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The funding type. For card transactions, this is always ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Card"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fundingData"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Details about the payment method used for the transaction."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fundingData.card"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Details about the card."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fundingData.card.gatewayTokenId"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (UUID)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The gateway token ID from the initial transaction. This replaces the need to provide full card details."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amounts"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Details about the transaction amount."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amounts.transaction"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["number"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The transaction amount."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amounts.currencyCode"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (3 characters)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The currency code in ISO 4217 format."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recurring"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Details about the recurring payment."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recurring.processingModel"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string (enum)"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The processing model for the recurring payment. Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedSubsequentRecurring"]}," for standard subscription charges.",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},"Possible values:",{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedSubsequentRecurring"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedReAuthorisation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedResubmission"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedDelayedCharge"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MerchantInitiatedNoShow"]}]}]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"response-example","__idx":5},"children":["Response example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If your request is successful, you'll receive a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," response with the transaction state. You'll also receive a webhook notification."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"state\": \"Captured\",\n  \"stateData\": {},\n  \"approvalCode\": \"210693\",\n  \"merchantTransactionId\": \"sub-charge-456\",\n  \"systemTransactionId\": \"635b1b51-4a27-4d23-8c9f-8150ff7eb9dd\",\n  \"merchantTransactionDate\": \"2025-02-27T08:51:02.826Z\",\n  \"fundingData\": {\n    \"cardScheme\": \"Visa\",\n    \"maskedPrimaryAccountNumber\": \"411111******1111\",\n    \"expiryMonth\": \"09\",\n    \"expiryYear\": \"2025\",\n    \"gatewayTokenId\": \"5fbd77ce-02c1-40ed-94bc-1016660b7512\",\n    \"providerResponse\": {\n      \"provider\": \"pxpfinancial\",\n      \"code\": \"00\",\n      \"paymentAccountReference\": \"637607302178175469\",\n      \"authorisedAmount\": 9.99\n    }\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/transactions/initiate-transactions"},"children":["Learn more about initiating transactions via API"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"card-on-file-recurring-payments","__idx":6},"children":["Card-on-file recurring payments"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-1-set-up-the-initial-transaction","__idx":7},"children":["Step 1: Set up the initial transaction"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To save a card, you'll first need to ask for the customer's consent during the initial transaction. If the customer ticks the checkbox, their card details will be saved as a token for future reuse."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import PXPCheckoutSDK\n\nlet transactionData = TransactionData(\n    amount: Decimal(string: \"99.99\")!,\n    currency: \"USD\",\n    entryType: .ecom,\n    intent: TransactionIntentData(card: .authorisation),\n    merchantTransactionId: \"order-12345\",\n    merchantTransactionDate: { Date() }\n)\n\nlet checkoutConfig = CheckoutConfig(\n    environment: .test,\n    session: sessionData,\n    transactionData: transactionData,\n    merchantShopperId: \"customer-123\",\n    ownerType: \"MerchantGroup\",\n    ownerId: \"your-owner-id\",\n    onGetShopper: { async in\n        TransactionShopper(\n            email: \"customer@example.com\",\n            firstName: \"John\",\n            lastName: \"Doe\"\n        )\n    }\n)\n\nlet pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)\n\n// Create the card consent component\nlet consentConfig = CardConsentComponentConfig(\n    label: \"Save this card for faster checkout in the future\"\n)\nlet consentComponent = try pxpCheckout.create(.cardConsent, componentConfig: consentConfig) as! CardConsentComponent\n\n// Create the card input components\nlet cardNumberComponent = try pxpCheckout.create(.cardNumber, componentConfig: CardNumberComponentConfig(label: \"Card number\")) as! CardNumberComponent\n\nlet expiryComponent = try pxpCheckout.create(.cardExpiryDate, componentConfig: CardExpiryDateComponentConfig(label: \"Expiry date\")) as! CardExpiryDateComponent\n\nlet cvcComponent = try pxpCheckout.create(.cardCvc, componentConfig: CardCvcComponentConfig(label: \"CVC\")) as! CardCvcComponent\n\n// Create the card submit component\nvar submitConfig = CardSubmitComponentConfig()\nsubmitConfig.cardNumberComponent = cardNumberComponent\nsubmitConfig.cardExpiryDateComponent = expiryComponent\nsubmitConfig.cardCvcComponent = cvcComponent\nsubmitConfig.cardConsentComponent = consentComponent\nsubmitConfig.useCardOnFile = false\nsubmitConfig.submitText = \"Pay & save card\"\n\nsubmitConfig.onPostTokenisation = { result in\n    if let success = result as? CardTokenizationResultSuccess {\n        print(\"Card tokenised. Gateway Token ID: \\(success.gatewayTokenId)\")\n        \n        // Get full token details from backend if needed for validation\n        Task {\n            let tokenDetails = await getTokenDetailsFromBackend(success.gatewayTokenId)\n            print(\"Card saved for future use\")\n            \n            // Store the token reference in your system\n        }\n    } else if let failed = result as? CardTokenizationResultFailed {\n        print(\"Tokenisation failed: \\(failed.errorReason)\")\n    }\n}\n\nsubmitConfig.onPreAuthorisation = { _ async in\n    return TransactionInitiationData()\n}\n\nsubmitConfig.onPostAuthorisation = { result in\n    // Check result type - MerchantSubmitResult contains transaction details\n    if let merchantResult = result as? MerchantSubmitResult {\n        print(\"Payment successful, card saved for future use\")\n        print(\"System transaction ID: \\(merchantResult.systemTransactionId)\")\n    } else if let failed = result as? FailedSubmitResult {\n        print(\"Transaction failed: \\(failed.errorReason ?? \"Unknown error\")\")\n    }\n}\n\nlet submitComponent = try pxpCheckout.create(.cardSubmit, componentConfig: submitConfig) as! CardSubmitComponent\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"step-2-set-up-subsequent-transactions","__idx":8},"children":["Step 2: Set up subsequent transactions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For subsequent transactions with saved cards, you can choose from:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A traditional card selection:"]}," This is best for customers with multiple saved cards and provides"," ","full card management (edit, delete, update) but requires more interaction from the customer. It uses the card-on-file component."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A streamlined click-once checkout:"]}," This is ideal for customers with one primary payment method and offers a faster checkout experience, but less flexibility. It uses the standalone click-once component."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A hybrid approach:"]}," This combines the two components. It defaults to displaying a primary card, but allows customers to choose a different one if they want to."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When using card-on-file or click-once components, the SDK automatically sets the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["processingModel"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CardOnFileShopperInitiated"]}," for subsequent transactions."]}]},{"$$mdtype":"Tag","name":"CodeGroup","attributes":{"mode":"tabs"},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","data-title":"Card-on-file","header":{"title":"Card-on-file","controls":{"copy":{}}},"source":"var cofConfig = CardOnFileComponentConfig()\ncofConfig.limitTokens = 10\ncofConfig.orderBy = OrderByConfig(\n    lastUsageDate: OrderByLastUsageDateConfig(\n        orderByField: \"lastSuccessfulPurchaseDate\",\n        direction: \"desc\",\n        priority: 1\n    )\n)\ncofConfig.deleteCardButtonAccessibilityLabel = \"Delete this card\"\ncofConfig.editCardInformationAccessibilityLabel = \"Edit card details\"\n\nlet cardOnFileComponent = try pxpCheckout.create(\n    .cardOnFile,\n    componentConfig: cofConfig\n) as! CardOnFileComponent\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","data-title":"Click-once standalone","header":{"title":"Click-once standalone","controls":{"copy":{}}},"source":"var clickOnceConfig = ClickOnceStandaloneComponentConfig()\nclickOnceConfig.limitTokens = 1\nclickOnceConfig.isCvcRequired = false\nclickOnceConfig.submitText = \"Complete purchase\"\nclickOnceConfig.disableCardSelection = true\n\nlet clickOnceComponent = try pxpCheckout.create(\n    .clickOnceStandalone,\n    componentConfig: clickOnceConfig\n) as! ClickOnceStandaloneComponent\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","data-title":"Hybrid","header":{"title":"Hybrid","controls":{"copy":{}}},"source":"// Primary checkout with click-once\nvar quickCheckoutConfig = ClickOnceStandaloneComponentConfig()\nquickCheckoutConfig.limitTokens = 1\nquickCheckoutConfig.submitText = \"Pay with Primary Card\"\n\nlet quickCheckout = try pxpCheckout.create(\n    .clickOnceStandalone,\n    componentConfig: quickCheckoutConfig\n) as! ClickOnceStandaloneComponent\n\n// Alternative with full card selection\nvar alternativeConfig = CardOnFileComponentConfig()\nalternativeConfig.limitTokens = 5\n\nlet alternativeCheckout = try pxpCheckout.create(\n    .cardOnFile,\n    componentConfig: alternativeConfig\n) as! CardOnFileComponent\n\n// Toggle between interfaces based on your UI logic\n","lang":"swift"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"using-the-api-for-backend-initiated-card-on-file-transactions","__idx":9},"children":["Using the API for backend-initiated card-on-file transactions"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["While the components above handle shopper-initiated transactions (where the customer is present and actively making a payment), you can also use the Transactions API to charge a saved card from your backend without the customer being present."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is useful for scenarios like:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Auto-renewing a service when the customer returns to your platform."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Charging for usage-based billing at the end of a billing period."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Processing payments for bookings or reservations."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To charge a saved card via API, use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CardOnFileShopperInitiated"]}," processing model with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["gatewayTokenId"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"merchant\": \"MERCHANT-1\",\n  \"site\": \"SITE-1\",\n  \"merchantTransactionId\": \"charge-001\",\n  \"merchantTransactionDate\": \"2025-02-27T08:51:02.826Z\",\n  \"transactionMethod\": {\n    \"intent\": \"Purchase\",\n    \"entryType\": \"Ecom\",\n    \"fundingType\": \"Card\"\n  },\n  \"fundingData\": {\n    \"card\": {\n      \"gatewayTokenId\": \"5fbd77ce-02c1-40ed-94bc-1016660b7512\"\n    }\n  },\n  \"amounts\": {\n    \"transaction\": 49.99,\n    \"currencyCode\": \"USD\"\n  },\n  \"recurring\": {\n    \"processingModel\": \"CardOnFileShopperInitiated\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/transactions/e-commerce/recurring-payments#set-up-a-card-on-file-recurring-payment"},"children":["Learn more about card-on-file payments via API"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"3ds-authentication-for-recurring-payments","__idx":10},"children":["3DS authentication for recurring payments"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For the initial customer-present setup transaction, you should use integrated 3DS authentication. Configure both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreInitiateAuthentication"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthentication"]}," callbacks. Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RequestorAuthenticationIndicatorType.recurringTransaction"]}," in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PreInitiateIntegratedAuthenticationData"]},", and include ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ThreeDSRecurring"]}," on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InitiateIntegratedAuthenticationData"]}," so the issuer understands the recurring payment frequency and agreement expiration."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"var submitConfig = CardSubmitComponentConfig()\n\nsubmitConfig.onPreInitiateAuthentication = {\n    return PreInitiateIntegratedAuthenticationData(\n        providerId: \"your_3ds_provider_id\",\n        requestorAuthenticationIndicator: .recurringTransaction,\n        timeout: 120\n    )\n}\n\nsubmitConfig.onPreAuthentication = {\n    return InitiateIntegratedAuthenticationData(\n        merchantCountryNumericCode: \"826\",\n        merchantLegalName: \"Your Company Ltd\",\n        challengeWindowSize: .size5,\n        requestorChallengeIndicator: .noPreference,\n        recurring: ThreeDSRecurring(\n            expirationDate: Calendar.current.date(byAdding: .year, value: 1, to: Date()),\n            frequencyInDays: 30\n        ),\n        timeout: 300\n    )\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For subsequent merchant-initiated charges, you typically don't need 3DS authentication. Leave both 3DS callbacks unset, or return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nil"]}," from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreInitiateAuthentication"]}," if both callbacks are configured. For card-on-file MIT flows, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionInitiatorType = .MIT"]}," on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CardOnFileComponentConfig"]}," to indicate the transaction was merchant-initiated."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/ios/card/3ds"},"children":["Learn more about 3DS transactions"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"errors-and-retries","__idx":11},"children":["Errors and retries"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onSubmitError"]},": Returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BaseSdkException"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["errorCode"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["errorMessage"]}," for token vault, validation, network, and similar errors."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthorisation"]},": Returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AuthorisedSubmitResult"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CapturedSubmitResult"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RefusedSubmitResult"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FailedSubmitResult"]},". Treat a normal issuer decline as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["RefusedSubmitResult"]},"."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"submitConfig.onSubmitError = { error in\n    print(\"Error code: \\(error.errorCode)\")\n    print(\"Error message: \\(error.errorMessage)\")\n}\n\nsubmitConfig.onPostAuthorisation = { result in\n    switch result {\n    case is AuthorisedSubmitResult, is CapturedSubmitResult:\n        print(\"Transaction successful\")\n    case let refused as RefusedSubmitResult:\n        print(\"Transaction refused: \\(refused.stateData?.message ?? \"\")\")\n    case let failed as FailedSubmitResult:\n        print(\"Transaction failed: \\(failed.errorReason ?? \"Unknown error\")\")\n    default:\n        print(\"Unknown result type\")\n    }\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use a new ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionId"]}," (and refreshed ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TransactionData"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CheckoutConfig"]},") for each retry attempt. For soft-decline handling, use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreRetrySoftDecline"]}," callback on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CardSubmitComponentConfig"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/ios/card/card-submit"},"children":["Learn more about card submit configuration"]},"."]}]},"headings":[{"value":"Recurring payments","id":"recurring-payments","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Merchant-initiated recurring payments","id":"merchant-initiated-recurring-payments","depth":2},{"value":"Step 1: Set up the initial recurring payment","id":"step-1-set-up-the-initial-recurring-payment","depth":3},{"value":"Step 2: Charge subsequent recurring payments","id":"step-2-charge-subsequent-recurring-payments","depth":3},{"value":"Response example","id":"response-example","depth":4},{"value":"Card-on-file recurring payments","id":"card-on-file-recurring-payments","depth":2},{"value":"Step 1: Set up the initial transaction","id":"step-1-set-up-the-initial-transaction","depth":3},{"value":"Step 2: Set up subsequent transactions","id":"step-2-set-up-subsequent-transactions","depth":3},{"value":"Using the API for backend-initiated card-on-file transactions","id":"using-the-api-for-backend-initiated-card-on-file-transactions","depth":3},{"value":"3DS authentication for recurring payments","id":"3ds-authentication-for-recurring-payments","depth":2},{"value":"Errors and retries","id":"errors-and-retries","depth":2}],"frontmatter":{"seo":{"title":"Recurring payments"}},"lastModified":"2026-06-12T11:56:36.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/ios/card/recurring-payments","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}