# How 3D Secure works on PayMe Building with an AI agent? Start at https://docs.payme.io/agents.md ## Prerequisites - The 3D Secure service active on the seller's account (PayMe enables it) ## Related - [Create a sale with 3D Secure](/payments/generate-a-sale-with-3d-secure/post-generate-sale-generate-a-sale-with-3d-secure) - [3DS with Hosted Fields](/accepting-payments/3d-secure/3ds-with-hosted-fields) - [3D Secure as a service](/accepting-payments/3d-secure/3d-secure-as-a-service) - [Standalone 3D Secure service](/payments/standalone-3d-secure-service) - [Pay a sale](/payments/direct-api/post-pay-sale) - [Accept Google Pay](/accepting-payments/payment-methods/google-pay) - [Test Cards and Payment Methods](/payments/getting-started/test-cards-and-payment-methods) - [Callbacks](/payments/getting-started/callbacks) - [Errors](/payments/getting-started/errors) - [Understand the sale lifecycle](/payments/codes-statuses/sale-lifecycle) 3D Secure (3DS) asks the card issuer to confirm that the buyer is the cardholder before the card is charged. PayMe runs 3D Secure 2 (version 2.2.0). The issuer either approves without asking the buyer anything (**frictionless**), or shows a **challenge**, such as a one-time code. This page explains when PayMe runs 3D Secure, what each integration has to do, how to test it, and how to read the result. The step-by-step pages it links to have the full request and response fields. ## When 3D Secure runs A card payment gets 3D Secure when all three conditions hold. Google Pay has its own rule, described [below](#google-pay). 1. **The seller's account has the 3D Secure service.** PayMe enables it. An account that has never had it gets no 3D Secure. 2. **The card brand is supported.** PayMe's 3D Secure provider doesn't support Discover, JCB or Isracard cards, so those cards are charged without 3D Secure. 3. **A rule selects the payment.** Any one of these is enough: - **PayMe's system rules:** a card issued in a country other than the seller's registered country; 3 or more installments; or an amount of at least the account's 3D Secure threshold. The amount rule compares `sale_price` in minor units with the threshold, without converting currencies, so the same number applies to every currency. - **Rules of your own**, which PayMe sets on the account. Ask your onboarding manager before you go live. - **The per-sale setting** described below. 3D Secure applies to card payments: `credit-card`, a card the buyer chose on a `multi` page, and Google Pay cards saved in the buyer's Google account (see [Google Pay](#google-pay)). It runs on `authorize` sales as well as on ordinary sales. These payments never go through 3D Secure: - A charge on a saved card with `buyer_key` on `generate-sale` (see [Save a card with a token sale](/accepting-payments/capture-and-saved-cards/save-a-card-and-charge-it-later#3-charge-the-saved-card)). - Subscription charges. - A token sale (`sale_type: token`), which saves the card without charging it. ### Turn it on or off for one sale Send `services` with the `3DSecure` service when you create the sale with [Create a sale with 3D Secure](/payments/generate-a-sale-with-3d-secure/post-generate-sale-generate-a-sale-with-3d-secure): ```json { "services": [{"name": "3DSecure", "settings": {"active": true}}] } ``` - `"active": true` forces 3D Secure on this sale, while the account's 3D Secure service is turned on. - `"active": false` turns off your account's own rules for this sale. PayMe's system rules still apply: a foreign card, 3 or more installments or an amount at or above the threshold still gets 3D Secure. You can't skip them for a single sale, for example a phone order. Only PayMe can switch the system rules off for the account. - Turning the 3D Secure service off doesn't take the account out of 3D Secure: PayMe's system rules still apply, and the per-sale setting has no effect. - Each entry needs `settings`: an entry without it returns error `1` after the sale has already been created. A setting for a service the account doesn't have is ignored, without an error. ## What each integration does | Integration | Who collects the browser data | How the challenge reaches the buyer | Details | |---|---|---|---| | Hosted payment page (`sale_url`) | PayMe's page | PayMe's page shows it | [Below](#hosted-payment-page) | | Hosted Fields | `PayMe.clientData()` in your page, sent as `meta_data_jwt` | `pay-sale` returns `status_code: 5` and `redirect_url` | [3DS with Hosted Fields](/accepting-payments/3d-secure/3ds-with-hosted-fields) | | Direct API (`pay-sale` with card fields) | You, sent as `meta_data_jwt` | `status_code: 5` and `redirect_url` | [Below](#direct-api-pay-sale) | | Google Pay (`googleSale()`) | PayMe's library | `paymentCompleted` gets `status_code: 5` and `redirect_url` | [Accept Google Pay](/accepting-payments/payment-methods/google-pay#step-5-handle-the-result) | | Standalone 3D Secure | PayMe's JS library | `status_code: 5` and `redirect_url` | [Standalone 3D Secure](/payments/standalone-3d-secure-service) | ### Hosted payment page There's nothing extra to build. When a rule selects the payment, PayMe's page collects the browser data and shows the challenge. 3D Secure also works when you show `sale_url` in an iframe, but some issuing banks block their own challenge screen inside an iframe, so keep a fallback that opens the page in a full window. See [Hosted Payment Page options](/accepting-payments/checkout-and-in-person/hosted-payment-page-options#showing-sale_url-in-an-iframe). - **The buyer passes:** PayMe charges the card and sends `sale-complete` (or `sale-authorized` for an `authorize` sale) with `sale_3ds` set to `1`. The buyer lands on the sale's return URL. - **The buyer fails the challenge:** the sale becomes `failed` and PayMe sends `sale-failure` with `status_error_code` `21017`. If the sale has a return URL (`sale_return_url`, or your partner's default), the buyer lands there with `payme_status=error` and `status_error_code=21017`. Without a return URL, the buyer goes to `cancel_url` if you sent one, and otherwise back to the payment page to try another card. A failed attempt doesn't close the sale: `sale_url` stays payable until the sale is paid, or until `sale_expiry_seconds` passes if you set it. See [Understand the sale lifecycle](/payments/codes-statuses/sale-lifecycle). ### Hosted Fields Follow [3DS with Hosted Fields](/accepting-payments/3d-secure/3ds-with-hosted-fields). In short: 1. Tokenize the card with Hosted Fields. 2. In the same page, call `PayMe.clientData(testMode)` and send the `hash` it returns to your server. 3. Create the sale with `generate-sale` **without** a `buyer_key`, and set `sale_callback_url` and `sale_return_url` there. 4. Call [`pay-sale`](/payments/direct-api/post-pay-sale) with the `payme_sale_id`, the token as `buyer_key`, and the hash as `meta_data_jwt`. 5. Handle the result: `status_code` `5` means send the buyer's browser to `redirect_url`; `0` paid; `1` declined. **Always send `meta_data_jwt`.** Without it, `pay-sale` charges the token without 3D Secure, and nothing in the response tells you so. ### Direct API (`pay-sale`) The [Direct API](/payments/direct-api) (PCI DSS Level 1 partners only) sends the card fields to [`pay-sale`](/payments/direct-api/post-pay-sale). PayMe runs 3D Secure inside the same call when a rule selects the payment: - Send the buyer's browser data as `meta_data_jwt`, collected in the buyer's browser with `PayMe.clientData()`. Without browser data, the card is charged without 3D Secure. The `meta_data` object takes the IP address and `Accept` header from the request itself, so use it only on a call made from the buyer's browser. - `status_code` `5` with `redirect_url` means the issuer wants a challenge. Send the buyer's browser (the top window) to `redirect_url`; `status_message` is a message you can show meanwhile. The result arrives in the sale callback, and the buyer lands on `sale_return_url`. - `status_code` `0` means paid, and `sale_3ds` tells you whether 3D Secure ran. `status_code` `1` is a decline, returned with HTTP 200. - If you already ran 3D Secure with another provider, send its `xid`, `eci`, `cavv` and `3ds_version`. PayMe then doesn't run its own. ### Google Pay Whether 3D Secure runs on a Google Pay payment depends on the card, not on the rules or the device: - A card stored on the buyer's device (Google's `CRYPTOGRAM_3DS`) is charged directly, without PayMe's 3D Secure. - A card saved in the buyer's Google account (`PAN_ONLY`) always goes through PayMe's 3D Secure, whatever the rules and the per-sale setting say. This needs the seller's 3D Secure service. Without it, or when 3D Secure can't run for the card, the payment fails with error `21017` "Verification failed, please try again". With the `googleSale()` button, a payment that needs a challenge calls `paymentCompleted(null, sale)` with `sale.status_code` `5` and `sale.redirect_url`. The sale isn't paid yet: send the buyer to `redirect_url`. See [Accept Google Pay](/accepting-payments/payment-methods/google-pay#step-5-handle-the-result). ### Standalone 3D Secure Use it to authenticate a card without charging it: when another gateway processes the payment, or when you authenticate now and charge later. The flow is on [Standalone 3D Secure: process flow](/payments/standalone-3d-secure-service), and the background on [3D Secure as a service](/accepting-payments/3d-secure/3d-secure-as-a-service): 1. Collect the browser data with PayMe's JS library and create a card sale. 2. From the browser, call [Start 3D Secure authentication](/payments/standalone-3d-secure-service/post-initialize-3dsecure-request). A frictionless approval returns `hash_3dsecure`; a challenge returns `status_code` `5` and `redirect_url`. 3. From your server, call [Resolve a 3D Secure hash](/payments/standalone-3d-secure-service/get-sales-payme-sale-id-3ds-hash) to get `eci`, `cavv` and `xid` for your gateway. If authentication failed, the result has a non-null `status` and no `cavv`. The seller needs the 3D Secure service (otherwise error `720`), and the call needs the browser data (otherwise error `670`). ## Test it in the sandbox The sandbox applies the same rules as production, so a rule must select the sale. `4918914107195005` is issued abroad, so the foreign-card rule selects it at any amount. For the other two cards, make sure a rule selects the sale, for example by forcing 3D Secure with `services` (`"active": true`). All three cards use expiry `07/28` and CVV `123`. | Card | What happens | What you see | |---|---|---| | `4918914107195005` | Challenge. Enter `555` to pass; any other code (for example `111`) fails. | Pass: `completed`, `sale_3ds` `1`. Fail: `failed`, error `21017` | | `4907639999909022` | Frictionless approval, no challenge | `completed`, `sale_3ds` `1` | | `5283901906612672` | Frictionless decline, no challenge | `failed`, error `21017` | The full sandbox list is on [Test Cards and Payment Methods](/payments/getting-started/test-cards-and-payment-methods#3d-secure-in-the-sandbox). ## Read the result | Where | Field | Meaning | |---|---|---| | Sale callback | `sale_3ds` | `1` when the payment went through 3D Secure | | `pay-sale` response | `sale_3ds` | `true` when the payment passed 3D Secure | | [`get-sales`](/payments/query-apis/post-get-sales) and [`get-transactions`](/payments/query-apis/post-get-transactions) | `sale_is_3ds` | `true` if the payment used 3D Secure. Also a filter: `"sale_is_3ds": true` returns only those payments | | Standalone: Resolve a 3D Secure hash | `eci`, `cavv`, `xid` | The values to send to your gateway | Before you fulfil an order, confirm the sale with the callback or with `get-sales`, as described on [Callbacks](/payments/getting-started/callbacks#confirm-the-sale-before-you-fulfil). ### Failures and error codes | What happened | What you get | |---|---| | The buyer failed the challenge | The sale becomes `failed`. `sale-failure` with `status_error_code` `21017` ("Verification failed, please try again"). The return-URL redirect carries `payme_status=error` | | The issuer declined without a challenge | `pay-sale` returns HTTP 200 with `status_code` `1` and `status_error_code` `21017`. The sale becomes `failed`, and PayMe sends `sale-failure` | | The card or its issuer can't run 3D Secure | By default PayMe charges the card without 3D Secure, and `sale_3ds` is `0`. If PayMe has set your account to refuse such payments, or for a Google Pay `PAN_ONLY` card, the payment fails with `21017` | | No browser data on `pay-sale` | No error: the card is charged without 3D Secure | | 3D Secure is required, but no terminal on the account can run it | Error `420` with `3DS` in `status_additional_info`. Contact PayMe support | | Standalone: the seller has no 3D Secure service | Error `720` | | Standalone: the browser data is missing or unreadable | Error `670` | A failed 3D Secure attempt is an ordinary failed attempt: the buyer can try again on the same sale. Error codes and the HTTP status of each are on [Errors](/payments/getting-started/errors).