# Save a card as a token Building with an AI agent? Start at https://docs.payme.io/agents.md - Sandbox: `POST https://sandbox.payme.io/api/capture-buyer-token` - Production: `POST https://live.payme.io/api/capture-buyer-token` ## Credentials - Request body field `seller_payme_id` (required): The seller's MPL. ## Overview Saves a card and returns a `buyer_key` you can charge later. Nothing is charged. You send raw card details, so call it from your server only if you are PCI DSS Level 1 certified. Otherwise save cards with [Hosted Fields](/payments/hosted-fields-jsapi) or a [`token` sale](/accepting-payments/capture-and-saved-cards/save-a-card-and-charge-it-later). Name the seller with `seller_payme_id`. (Hosted Fields sends the seller's public key in the `PayMe-Public-Key` header instead.) ### Two flags decide what the token is - `buyer_is_permanent: true` makes a token you can charge any number of times. Without it the token is single-use: the first `generate-sale` charge with it deactivates it, later charges fail with error `604`, and subscriptions reject it. - `buyer_is_private: true` ties the token to this seller (MPL). Without it the token belongs to your account, and any seller of the same partner can charge it. To read such a token back with [`get-buyer-key`](/payments/tokenization/post-get-buyer-key) you need `payme_client_key`. ### Charging the token Send the `buyer_key` with [`generate-sale`](/payments/hosted-payment-page/post-generate-sale) to charge the card at once, with no payment page and no CVV. These charges run without 3D Secure. A permanent token doesn't expire in PayMe, but a charge can still fail if the card has expired or the issuer declines it, so handle the `sale-failure` callback. Store `buyer_key` encrypted: it stands in for the card number. Saving the same card again for the same buyer and seller can return the existing `buyer_key`. ### Errors A declined card returns HTTP 500, with the decline code in `status_error_code` (for example `20004`) and the masked card number in `status_additional_info`. This differs from `pay-sale`, which returns HTTP 200 for a decline. A value that looks like a card number in any field other than `credit_card_number` returns HTTP 400, error `20`. ## Parameters This endpoint takes no path, query or header parameters. ## Request body Content type: `application/json` | Field | Type | Required | Description | | --- | --- | --- | --- | | `seller_payme_id` | string | yes | The seller's MPL. | | `credit_card_number` | string | yes | Card number, digits only. | | `credit_card_exp` | string | yes | Card expiry as `MMYY` (`MM/YY` and `MMYYYY` are also accepted). An expired or invalid date returns error `356`. | | `credit_card_cvv` | string | no | CVV, 3 or 4 digits. Required unless `token_terminal` is `true`. | | `token_terminal` | boolean | no | `true` to save the card without a CVV, on a token terminal. Default `false`. | | `buyer_name` | string | no | Buyer's full name. | | `buyer_email` | string | no | Buyer's email address. | | `buyer_phone` | string | no | Buyer's phone number. | | `buyer_social_id` | string | no | Buyer's national ID number, digits only. | | `buyer_zip_code` | string | no | Buyer's zip code, letters and digits only; anything else returns error `37`. | | `buyer_business_code` | string | no | For a business buyer: its registration number. Send it with `buyer_business_name`, or neither. | | `buyer_business_name` | string | no | For a business buyer: its name. Send it with `buyer_business_code`, or neither. | | `buyer_is_permanent` | boolean | no | `true`: a permanent token you can charge any number of times. Default `false`: a single-use token; the first `generate-sale` charge deactivates it and later charges fail with error `604`. Subscriptions accept permanent tokens only. | | `buyer_is_private` | boolean | no | `true`: only this seller (MPL) can charge the token. Default `false`: the token belongs to your account, and any seller of the same partner can charge it. | | `buyer_perform_validation` | boolean | no | Check the card with the issuer before saving it. Default `true`. | | `language` | string | no | Language of error messages: `en`, or `he` (the default). `es`, `ru` and `ar` are accepted, but messages then come back mostly in English. Any other value returns error `51`. | ### Request example ```json { "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "credit_card_number": "5326105300985846", "credit_card_exp": "1230", "credit_card_cvv": "658", "buyer_name": "Israel Israeli", "buyer_email": "buyer@example.com", "buyer_phone": "+972500000000", "buyer_social_id": "008336174", "buyer_is_permanent": true, "language": "en" } ``` ## Code samples ### curl ```bash curl -X POST 'https://sandbox.payme.io/api/capture-buyer-token' \ -H 'Content-Type: application/json' \ --data-raw '{ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "credit_card_number": "5326105300985846", "credit_card_exp": "1230", "credit_card_cvv": "658", "buyer_name": "Israel Israeli", "buyer_email": "buyer@example.com", "buyer_phone": "+972500000000", "buyer_social_id": "008336174", "buyer_is_permanent": true, "language": "en" }' ``` ### Node.js ```javascript const response = await fetch("https://sandbox.payme.io/api/capture-buyer-token", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "credit_card_number": "5326105300985846", "credit_card_exp": "1230", "credit_card_cvv": "658", "buyer_name": "Israel Israeli", "buyer_email": "buyer@example.com", "buyer_phone": "+972500000000", "buyer_social_id": "008336174", "buyer_is_permanent": true, "language": "en" }), }); console.log(await response.text()); ``` ### Python ```python import requests response = requests.post( "https://sandbox.payme.io/api/capture-buyer-token", json={ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "credit_card_number": "5326105300985846", "credit_card_exp": "1230", "credit_card_cvv": "658", "buyer_name": "Israel Israeli", "buyer_email": "buyer@example.com", "buyer_phone": "+972500000000", "buyer_social_id": "008336174", "buyer_is_permanent": True, "language": "en", }, ) print(response.text) ``` ### PHP ```php 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => $body, ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ## Responses ### 200 — The card is saved. (success) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `0`: the card is saved. | | `buyer_key` | string | The token. Send it as `buyer_key` on `generate-sale`. | | `buyer_name` | string | Buyer's full name. | | `buyer_email` | string | Buyer's email address. | | `buyer_phone` | string | Buyer's phone number. | | `buyer_card_mask` | string | Masked card number, for example `532610******5846`. | | `buyer_card_exp` | string | Card expiry, `MMYY`. | | `buyer_social_id` | string | Buyer's national ID number. | | `buyer_is_permanent` | boolean | `true` for a permanent token, `false` for a single-use token. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 0, "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-1234567", "buyer_name": "Israel Israeli", "buyer_email": "buyer@example.com", "buyer_phone": "+972500000000", "buyer_card_mask": "532610******5846", "buyer_card_exp": "1230", "buyer_social_id": "008336174", "buyer_is_permanent": true, "session": "Ab12Cd34" } ``` ### 400 — Error `20`: a card-like number in a field other than `credit_card_number`. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1`: the request failed. | | `status_error_code` | integer | PayMe's error code. See [Errors](/payments/getting-started/errors). | | `status_error_details` | string | The error message, in the language you asked for. | | `status_additional_info` | string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 1, "status_error_details": "Validation error", "status_additional_info": "buyer_name contains invalid data", "status_error_code": 20, "session": "Ab12Cd34" } ``` ### 500 — Request errors and declines. `1`: a required field is missing (named in `status_additional_info`). `251`: unknown seller. `354`, `355`, `356`: invalid CVV, card number or expiry. `37`: invalid `buyer_zip_code`. A decline returns its decline code, such as `20004`, with the masked card number in `status_additional_info`. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1`: the request failed. | | `status_error_code` | integer | PayMe's error code. See [Errors](/payments/getting-started/errors). | | `status_error_details` | string | The error message, in the language you asked for. | | `status_additional_info` | string | Extra detail: usually the field at fault, the sale's current status, or the masked card number. | | `session` | string | PayMe's ID for this request. Quote it when you contact PayMe. | ```json { "status_code": 1, "status_error_details": "Refusal by credit company. Please try a different card", "status_additional_info": "400000******0002", "status_error_code": 20004, "session": "Ab12Cd34" } ```