> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.coinflow.cash/guides/checkout/payment-scenarios/subsequent-transactions/apple-pay/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.coinflow.cash/_mcp/server. # 📱 Apple Pay Subsequent Transactions ## Overview Coinflow supports **Card on File (COF)** transactions for Apple Pay. Once a customer completes an initial Apple Pay purchase, Coinflow securely saves their Apple Pay card so you can process subsequent, customer-initiated purchases **without showing the Apple Pay sheet again** — no re-authentication with Face ID or Touch ID required. This uses the same [Card on File Checkout endpoint](/api-reference/api-reference/checkout/card-on-file-checkout) as regular card payments. Coinflow automatically detects that the original payment was made with Apple Pay — no additional parameters are required. > **Info** > > **Customer Initiated Only** > > Apple Pay subsequent transactions are supported for **Card on File (Customer Initiated) transactions only**. [Merchant Initiated Transactions (MIT)](/guides/checkout/payment-scenarios/subsequent-transactions/merchant-initiated-transactions) — such as automated subscription renewals — are not supported with Apple Pay. The customer must be present and actively confirming each purchase. --- ## How Apple Pay Cards Are Saved When **Card on File is enabled** on your merchant account, every successful Apple Pay checkout automatically saves the customer's card: ### Customer Pays with Apple Pay The customer completes a purchase through the Apple Pay sheet, authenticating with Face ID or Touch ID. ### Coinflow Saves the Card Coinflow tokenizes the card behind the Apple Pay payment and stores it securely in Coinflow's PCI-compliant vault, attached to the customer's profile. ### Card Appears on the Customer Profile The saved card is returned in the `mobiles` array of the [Get Customer](/api-reference/api-reference/customers/get-customer) response, including a display alias (e.g. "Visa 1234"), the card `token`, and expiration details. ### Request GET [https://api-sandbox.coinflow.cash/api/customer/v2](https://api-sandbox.coinflow.cash/api/customer/v2) **`Saved Apple Pay Cards`** ```curl Saved Apple Pay Cards curl https://api-sandbox.coinflow.cash/api/customer/v2 \ -H "x-coinflow-auth-session-key: " ``` **`Saved Apple Pay Cards`** ```python Saved Apple Pay Cards import requests url = "https://api-sandbox.coinflow.cash/api/customer/v2" headers = {"x-coinflow-auth-session-key": ""} response = requests.get(url, headers=headers) print(response.json()) ``` **`Saved Apple Pay Cards`** ```javascript Saved Apple Pay Cards const url = 'https://api-sandbox.coinflow.cash/api/customer/v2'; const options = {method: 'GET', headers: {'x-coinflow-auth-session-key': ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` **`Saved Apple Pay Cards`** ```go Saved Apple Pay Cards package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api-sandbox.coinflow.cash/api/customer/v2" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("x-coinflow-auth-session-key", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`Saved Apple Pay Cards`** ```ruby Saved Apple Pay Cards require 'uri' require 'net/http' url = URI("https://api-sandbox.coinflow.cash/api/customer/v2") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["x-coinflow-auth-session-key"] = '' response = http.request(request) puts response.read_body ``` **`Saved Apple Pay Cards`** ```java Saved Apple Pay Cards import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api-sandbox.coinflow.cash/api/customer/v2") .header("x-coinflow-auth-session-key", "") .asString(); ``` **`Saved Apple Pay Cards`** ```php Saved Apple Pay Cards request('GET', 'https://api-sandbox.coinflow.cash/api/customer/v2', [ 'headers' => [ 'x-coinflow-auth-session-key' => '', ], ]); echo $response->getBody(); ``` **`Saved Apple Pay Cards`** ```csharp Saved Apple Pay Cards using RestSharp; var client = new RestClient("https://api-sandbox.coinflow.cash/api/customer/v2"); var request = new RestRequest(Method.GET); request.AddHeader("x-coinflow-auth-session-key", ""); IRestResponse response = client.Execute(request); ``` **`Saved Apple Pay Cards`** ```swift Saved Apple Pay Cards import Foundation let headers = ["x-coinflow-auth-session-key": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/customer/v2")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Response (200) ```json { "customer": { "email": "string", "createdAt": "2024-01-15T09:30:00Z", "_id": "string", "merchant": "string", "customerId": "string", "bankAccounts": [ { "last4": "string", "accountHash": "string", "alias": "string", "token": "string", "reference": "string" } ], "cards": [ { "last4": "string", "token": "string", "type": "VISA", "disbursementStatus": "Immediate", "createdAt": "2024-01-15T09:30:00Z", "hasAddress": true } ], "sepas": [ { "last4": "string", "accountHash": "string", "alias": "string", "token": "string", "reference": "string", "sortCode": "string" } ], "fasterPayments": [ { "last4": "string", "accountHash": "string", "alias": "string", "token": "string", "reference": "string", "sortCode": "string" } ], "interacs": [ { "alias": "string", "token": "string", "type": "interac" } ], "mobiles": [ { "alias": "Visa 1234", "token": "4111114324324111_bt", "type": "mobile", "genus": "applepay", "hasAddress": true, "expMonth": "12", "expYear": "28" } ] } } ``` > **Success** > > **No Extra Integration Work for Saving** > > Card saving happens automatically on every successful Apple Pay purchase when Card on File is enabled — there is no separate "save card" call. --- ## Prerequisites Before processing Apple Pay subsequent transactions, make sure: 1. **Apple Pay is implemented** — See [Implement Apple Pay](/guides/checkout/payment-methods/payment-methods/apple-pay/implement-apple-pay#implementation) 2. **Card on File is enabled** on your merchant account — contact your Coinflow integration representative to enable it and configure your velocity limits 3. **An initial Apple Pay purchase is complete** — you need either the `paymentId` from that purchase or the saved card `token` from the customer profile > **Warning** > > **Processing Configuration** > > Apple Pay Card on File is not available on every processing configuration. If your requests return `403 - Card on file not available`, contact your Coinflow integration representative. --- ## Implementation Guide ### Step 1: Complete the Initial Apple Pay Purchase The customer's first purchase must go through the standard Apple Pay flow, where they authenticate on their device. After a successful purchase, save either of the following identifiers: * The `paymentId` from the checkout response or [webhook](/guides/developer-resources/webhooks/checkout-webhooks) * The card `token` from the `mobiles` array on the [Get Customer](/api-reference/api-reference/customers/get-customer) response ### Step 2 (Optional): Verify the Card Is Authorized Before starting a session that depends on subsequent transactions (for example, an auction or a tab), confirm the saved card can still be charged: ### Request POST [https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized](https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized) ```curl curl -X POST https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized \ -H "x-coinflow-auth-user-id: " \ -H "Content-Type: application/json" \ -d '{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" }' ``` ```python import requests url = "https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized" payload = { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" } headers = { "x-coinflow-auth-user-id": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized'; const options = { method: 'POST', headers: {'x-coinflow-auth-user-id': '', 'Content-Type': 'application/json'}, body: '{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized" payload := strings.NewReader("{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("x-coinflow-auth-user-id", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["x-coinflow-auth-user-id"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized") .header("x-coinflow-auth-user-id", "") .header("Content-Type", "application/json") .body("{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}") .asString(); ``` ```php request('POST', 'https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized', [ 'body' => '{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" }', 'headers' => [ 'Content-Type' => 'application/json', 'x-coinflow-auth-user-id' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized"); var request = new RestRequest(Method.POST); request.AddHeader("x-coinflow-auth-user-id", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "x-coinflow-auth-user-id": "", "Content-Type": "application/json" ] let parameters = ["token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXJkSWQiOiIxMjM0NTY3ODkwIiwidXNlcklkIjoiYWJjZGVmMTIzNDU2IiwiaWF0IjoxNjgwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/checkout/card-on-file-authorized")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Response (200) ```json { "authorized": true } ``` If `authorized` is `false`, direct the customer through a fresh Apple Pay purchase to create a new original payment reference. ### Step 3: Process the Subsequent Transaction Call the Card on File Checkout endpoint with either the original Apple Pay `paymentId` or the saved card `token`: [View Card on File Checkout API Reference](/api-reference/api-reference/checkout/card-on-file-checkout) **Using the Original Payment ID:** ### Request POST [https://api-sandbox.coinflow.cash/api/checkout/card-on-file](https://api-sandbox.coinflow.cash/api/checkout/card-on-file) **`Card Token`** ```curl Card Token curl -X POST https://api-sandbox.coinflow.cash/api/checkout/card-on-file \ -H "x-coinflow-auth-user-id: " \ -H "Content-Type: application/json" \ -d '{ "subtotal": { "cents": 2500, "currency": "USD" }, "token": "4111114324324111_bt" }' ``` **`Card Token`** ```python Card Token import requests url = "https://api-sandbox.coinflow.cash/api/checkout/card-on-file" payload = { "subtotal": { "cents": 2500, "currency": "USD" }, "token": "4111114324324111_bt" } headers = { "x-coinflow-auth-user-id": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` **`Card Token`** ```javascript Card Token const url = 'https://api-sandbox.coinflow.cash/api/checkout/card-on-file'; const options = { method: 'POST', headers: {'x-coinflow-auth-user-id': '', 'Content-Type': 'application/json'}, body: '{"subtotal":{"cents":2500,"currency":"USD"},"token":"4111114324324111_bt"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` **`Card Token`** ```go Card Token package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api-sandbox.coinflow.cash/api/checkout/card-on-file" payload := strings.NewReader("{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("x-coinflow-auth-user-id", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`Card Token`** ```ruby Card Token require 'uri' require 'net/http' url = URI("https://api-sandbox.coinflow.cash/api/checkout/card-on-file") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["x-coinflow-auth-user-id"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}" response = http.request(request) puts response.read_body ``` **`Card Token`** ```java Card Token import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api-sandbox.coinflow.cash/api/checkout/card-on-file") .header("x-coinflow-auth-user-id", "") .header("Content-Type", "application/json") .body("{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}") .asString(); ``` **`Card Token`** ```php Card Token request('POST', 'https://api-sandbox.coinflow.cash/api/checkout/card-on-file', [ 'body' => '{ "subtotal": { "cents": 2500, "currency": "USD" }, "token": "4111114324324111_bt" }', 'headers' => [ 'Content-Type' => 'application/json', 'x-coinflow-auth-user-id' => '', ], ]); echo $response->getBody(); ``` **`Card Token`** ```csharp Card Token using RestSharp; var client = new RestClient("https://api-sandbox.coinflow.cash/api/checkout/card-on-file"); var request = new RestRequest(Method.POST); request.AddHeader("x-coinflow-auth-user-id", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` **`Card Token`** ```swift Card Token import Foundation let headers = [ "x-coinflow-auth-user-id": "", "Content-Type": "application/json" ] let parameters = [ "subtotal": [ "cents": 2500, "currency": "USD" ], "token": "4111114324324111_bt" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/checkout/card-on-file")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` **Using the Card Token:** ### Request POST [https://api-sandbox.coinflow.cash/api/checkout/card-on-file](https://api-sandbox.coinflow.cash/api/checkout/card-on-file) **`Card Token`** ```curl Card Token curl -X POST https://api-sandbox.coinflow.cash/api/checkout/card-on-file \ -H "x-coinflow-auth-user-id: " \ -H "Content-Type: application/json" \ -d '{ "subtotal": { "cents": 2500, "currency": "USD" }, "token": "4111114324324111_bt" }' ``` **`Card Token`** ```python Card Token import requests url = "https://api-sandbox.coinflow.cash/api/checkout/card-on-file" payload = { "subtotal": { "cents": 2500, "currency": "USD" }, "token": "4111114324324111_bt" } headers = { "x-coinflow-auth-user-id": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` **`Card Token`** ```javascript Card Token const url = 'https://api-sandbox.coinflow.cash/api/checkout/card-on-file'; const options = { method: 'POST', headers: {'x-coinflow-auth-user-id': '', 'Content-Type': 'application/json'}, body: '{"subtotal":{"cents":2500,"currency":"USD"},"token":"4111114324324111_bt"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` **`Card Token`** ```go Card Token package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api-sandbox.coinflow.cash/api/checkout/card-on-file" payload := strings.NewReader("{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("x-coinflow-auth-user-id", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` **`Card Token`** ```ruby Card Token require 'uri' require 'net/http' url = URI("https://api-sandbox.coinflow.cash/api/checkout/card-on-file") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["x-coinflow-auth-user-id"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}" response = http.request(request) puts response.read_body ``` **`Card Token`** ```java Card Token import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api-sandbox.coinflow.cash/api/checkout/card-on-file") .header("x-coinflow-auth-user-id", "") .header("Content-Type", "application/json") .body("{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}") .asString(); ``` **`Card Token`** ```php Card Token request('POST', 'https://api-sandbox.coinflow.cash/api/checkout/card-on-file', [ 'body' => '{ "subtotal": { "cents": 2500, "currency": "USD" }, "token": "4111114324324111_bt" }', 'headers' => [ 'Content-Type' => 'application/json', 'x-coinflow-auth-user-id' => '', ], ]); echo $response->getBody(); ``` **`Card Token`** ```csharp Card Token using RestSharp; var client = new RestClient("https://api-sandbox.coinflow.cash/api/checkout/card-on-file"); var request = new RestRequest(Method.POST); request.AddHeader("x-coinflow-auth-user-id", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"subtotal\": {\n \"cents\": 2500,\n \"currency\": \"USD\"\n },\n \"token\": \"4111114324324111_bt\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` **`Card Token`** ```swift Card Token import Foundation let headers = [ "x-coinflow-auth-user-id": "", "Content-Type": "application/json" ] let parameters = [ "subtotal": [ "cents": 2500, "currency": "USD" ], "token": "4111114324324111_bt" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/checkout/card-on-file")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` **Response:** ### Response (200) ```json { "paymentId": "string" } ``` > **Info** > > **Example Values** > > The IDs, tokens, and amounts above are **examples only** and do not reflect real production values. Use the identifiers returned by your own checkout responses, webhooks, and customer profiles. > **Info** > > **Original Payment Must Be Apple Pay** > > The `originalPaymentId` must reference a successful **Apple Pay** purchase, and a `token` must belong to a saved Apple Pay card. You cannot mix payment sources — referencing a regular card payment for an Apple Pay saved card (or vice versa) returns a `400` error. ### Step 4: Handle Chargeback Protection (If Enabled) If chargeback protection is enabled on your account, include the Coinflow device ID header and `chargebackProtectionData` in the request, exactly as with regular [Card on File transactions](/guides/checkout/payment-scenarios/subsequent-transactions/card-on-file#step-3-handle-chargeback-protection-if-enabled). --- ## Handling Expired References (410 Errors) > **Warning** > > **Every integration must handle 410 responses** > > A `410 Gone` response means the original Apple Pay payment can no longer be used as a Card on File reference. Unlike regular card payments, there is no CVV re-entry fallback for Apple Pay — **the only way to recover is for the customer to complete a new purchase through the Apple Pay sheet**, which creates a fresh original payment reference. A `410` is returned when any of your Card on File velocity limits are hit: | Limit | Setting | When It Triggers | | ---------------- | --------------------- | ----------------------------------------------------------------------------- | | Payment count | `maxCount` / `period` | Too many Card on File payments against the original payment within the period | | Amount | `maxMultiple` | The transaction exceeds the allowed multiple of the original payment amount | | Reference window | `expiration` | Too much time has passed since the original Apple Pay purchase | **Recommended flow:** ```javascript async function chargeSavedApplePayCard({originalPaymentId, subtotal}) { const response = await fetch('/api/checkout/card-on-file', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'your-merchant-api-key', 'x-coinflow-auth-user-id': 'your-customer-id', }, body: JSON.stringify({subtotal, originalPaymentId}), }); if (response.status === 410) { // The reference is exhausted or expired. Show a friendly message and // re-present the Apple Pay sheet so the customer authenticates on-device. // The new purchase becomes the original payment reference going forward. return promptApplePayPurchase({subtotal}); } if (!response.ok) throw new Error((await response.json()).message); return response.json(); } ``` > **Tip** > > **Avoid 410s Mid-Session** > > Call [`POST /checkout/card-on-file-authorized`](/api-reference/api-reference/checkout/card-on-file-authorized) before starting a session that depends on subsequent transactions (an auction, a tab, a table). It runs the same velocity checks with a zero-amount total, so you can route the customer through a fresh Apple Pay purchase **before** they are mid-session rather than failing at charge time. --- ## Differences from Regular Card on File | Feature | Card on File (Regular Card) | Card on File (Apple Pay) | | ------------------------ | :--------------------------: | :--------------------------------------------------------------------: | | Endpoint | `/api/checkout/card-on-file` | `/api/checkout/card-on-file` | | CVV Required | No | No | | 3DS Eligible | Yes | No — the customer authenticated on-device during the original purchase | | Original Payment | CVV-verified card payment | Successful Apple Pay purchase | | Merchant Initiated (MIT) | Supported | Not supported | | Velocity Limits | Card on File settings | Same Card on File settings | Velocity limits (`maxCount`, `period`, `maxMultiple`, `expiration`) are shared with your regular [Card on File configuration](/guides/checkout/payment-scenarios/subsequent-transactions/card-on-file#merchant-configuration-settings). --- ## Error Handling #### 403 Forbidden - Card on File Not Enabled **Error Message:** ``` {merchantId} does not support card on file transactions ``` **Cause:** Card on File is not enabled or not configured on your merchant account. **Resolution:** Contact your Coinflow integration representative to enable Card on File. #### 403 Forbidden - Card on File Not Available **Error Message:** ``` Card on file not available. Please contact your integrations representative. ``` **Cause:** Your processing configuration does not support Apple Pay Card on File. **Resolution:** Contact your Coinflow integration representative. #### 400 Bad Request - Different Payment Source **Error Message:** ``` Cannot perform card on file operations with different payment source ``` **Cause:** The `originalPaymentId` references a payment made with a different payment source — for example, a regular card payment when charging a saved Apple Pay card, or a mobile wallet other than Apple Pay. **Resolution:** * Only reference an original payment made with **Apple Pay** * Google Pay payments cannot be used for subsequent transactions #### 400 Bad Request - Chained Card on File Payment **Error Message:** ``` Cannot perform card on file operations for a originalPaymentId which is a card on file transaction, please pass the originalPaymentId which processed with ApplePay ``` **Cause:** The `originalPaymentId` references a Card on File payment rather than the original Apple Pay purchase. **Resolution:** Always reference the initial Apple Pay purchase — do not chain Card on File transactions. #### 410 Gone - Velocity Limit Errors The original payment reference can no longer be used — a velocity limit was hit or the reference window expired. See [Handling Expired References](#handling-expired-references-410-errors) above for the recovery flow, and the [Card on File error reference](/guides/checkout/payment-scenarios/subsequent-transactions/card-on-file#error-handling) for the exact error messages. --- ## Frequently Asked Questions #### Does the customer need to re-authenticate with Face ID or Touch ID? No. Subsequent transactions run against the saved card without presenting the Apple Pay sheet. The customer's on-device authentication from the original purchase serves as the cardholder verification. #### Is Google Pay supported? No. Subsequent transactions are currently supported for Apple Pay only. Google Pay customers must authenticate through the Google Pay flow for every purchase. #### Can I use a saved Apple Pay card for subscriptions or other Merchant Initiated Transactions? No. Apple Pay saved cards support Customer Initiated (Card on File) transactions only — the customer must actively confirm each purchase in your application. #### How do I show the customer which card will be charged? Use the `mobiles` array on the [Get Customer](/api-reference/api-reference/customers/get-customer) response. Each saved Apple Pay card includes an `alias` (the card's display name from Apple Pay, e.g. "Visa 1234") you can show at confirmation. --- ## Next Steps #### [Implement Apple Pay](/guides/checkout/payment-methods/payment-methods/apple-pay/implement-apple-pay) Set up the initial Apple Pay purchase flow #### [Card on File](/guides/checkout/payment-scenarios/subsequent-transactions/card-on-file) Learn how Card on File works for regular card payments #### [Webhooks](/guides/developer-resources/webhooks/checkout-webhooks) Get notified about payment results #### [Merchant Initiated Transactions](/guides/checkout/payment-scenarios/subsequent-transactions/merchant-initiated-transactions) Automated charges with regular card payments