# Start 3D Secure authentication Building with an AI agent? Start at https://docs.payme.io/agents.md - Sandbox: `POST https://sandbox.payme.io/api/sales/{payme_sale_id}/3ds` - Production: `POST https://live.payme.io/api/sales/{payme_sale_id}/3ds` ## Credentials - Header `PayMe-Public-Key` (API key) ## Overview Starts 3D Secure 2 authentication for a card on an existing sale, without charging it. Use it when another gateway processes the payment and you need the 3DS values (`eci`, `cavv`, `xid`) from PayMe's MPI. The whole flow: [Standalone 3D Secure: process flow](/payments/standalone-3d-secure-service). Call it from the buyer's browser with the seller's public key in the `PayMe-Public-Key` header. The sale must belong to that seller. Send the browser data that PayMe's JS library collected (`meta_data_jwt`); see [3DS integration using JS API](/accepting-payments/3d-secure/3ds-with-hosted-fields). The response has one of two outcomes: - `status_code` `0`: frictionless approval. Pass `hash_3dsecure` to your server and resolve it with [Resolve a 3D Secure hash](/payments/standalone-3d-secure-service/get-sales-payme-sale-id-3ds-hash). - `status_code` `5`: the issuer wants a challenge. Send the buyer to `redirect_url` (a GET request). After the challenge, the hash comes back to the page that opened it. The seller must have the 3D Secure service active. ## Errors | HTTP | Code | When | |---|---|---| | 400 | `670` | `meta_data_jwt` (or `meta_data`) is missing or can't be read. | | 400 | `4` | The sale uses another payment method. | | 400 | `720` | 3D Secure was blocked for this card. | | 500 | `720` | 3D Secure isn't available for this card, or the seller has no 3D Secure service. | | 500 | `350` | No sale has this ID. | | 500 | Card error code | The card number or expiry is invalid, for example `356` for the expiry. | | 401 | — | The `PayMe-Public-Key` header is missing or unknown. | | 403 | `163` | The seller isn't active. | ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `payme_sale_id` | path | string | yes | The sale to authenticate (`payme_sale_id`). It must belong to the seller of the public key. | | `PayMe-Public-Key` | header | string | yes | The seller's public key. Safe to use in the browser. | ## Request body Content type: `application/json` | Field | Type | Required | Description | | --- | --- | --- | --- | | `payment` | object | yes | The card to authenticate. | | `payment.method` | string | yes | `credit-card`. | | `payment.card_number` | string | no | Card number. Required unless you send `buyer_key`. | | `payment.card_expiry` | string | no | Card expiry, `MMYY`. Required unless you send `buyer_key`. | | `payment.buyer_key` | string | no | A saved buyer token of this seller, instead of `card_number` and `card_expiry`. | | `customer` | object | no | Card holder details. Ignored when you send `payment.buyer_key`. | | `customer.name` | string | no | Card holder's name. | | `customer.email` | string | no | Card holder's email. | | `customer.phone` | string | no | Card holder's mobile number. | | `customer.social_id` | string | no | Card holder's national ID. | | `customer.zip_code` | string | no | Zip code. | | `customer.country` | string | no | Two-letter ISO country code. | | `customer.state` | string | no | State or region. | | `customer.city` | string | no | City. | | `customer.line1` | string | no | Address line 1. | | `customer.line2` | string | no | Address line 2. | | `meta_data_jwt` | string | no | The browser data from PayMe's JS library. Send this or `meta_data`; without either, the call fails with error `670`. | | `meta_data` | object | no | The browser data as a plain object, instead of `meta_data_jwt`. PayMe adds the buyer's IP and `Accept` header from the request, so send it from the buyer's browser. | | `meta_data.browser` | object | yes | Browser details: `userAgent`, `language`, `colorDepth`, `screen` (`height`, `width`), `timezone`, `javaEnabled` and `javaScriptEnabled`. | | `language` | string | no | Language of error messages: `he` (default), `en`, `es`, `ru` or `ar`. | ### Request example ```json { "payment": { "method": "credit-card", "card_number": "4111111111111111", "card_expiry": "1230" }, "customer": { "name": "Israel Israeli", "email": "buyer@example.com", "phone": "0500000000" }, "meta_data_jwt": "META_DATA_JWT_FROM_THE_LIBRARY" } ``` ## Code samples ### curl ```bash curl -X POST 'https://sandbox.payme.io/api/sales//3ds' \ -H 'Content-Type: application/json' \ -H 'PayMe-Public-Key: ' \ --data-raw '{ "payment": { "method": "credit-card", "card_number": "4111111111111111", "card_expiry": "1230" }, "customer": { "name": "Israel Israeli", "email": "buyer@example.com", "phone": "0500000000" }, "meta_data_jwt": "META_DATA_JWT_FROM_THE_LIBRARY" }' ``` ### Node.js ```javascript const response = await fetch("https://sandbox.payme.io/api/sales//3ds", { method: "POST", headers: { "Content-Type": "application/json", "PayMe-Public-Key": "", }, body: JSON.stringify({ "payment": { "method": "credit-card", "card_number": "4111111111111111", "card_expiry": "1230" }, "customer": { "name": "Israel Israeli", "email": "buyer@example.com", "phone": "0500000000" }, "meta_data_jwt": "META_DATA_JWT_FROM_THE_LIBRARY" }), }); console.log(await response.text()); ``` ### Python ```python import requests response = requests.post( "https://sandbox.payme.io/api/sales//3ds", headers={ "PayMe-Public-Key": "", }, json={ "payment": { "method": "credit-card", "card_number": "4111111111111111", "card_expiry": "1230", }, "customer": { "name": "Israel Israeli", "email": "buyer@example.com", "phone": "0500000000", }, "meta_data_jwt": "META_DATA_JWT_FROM_THE_LIBRARY", }, ) print(response.text) ``` ### PHP ```php /3ds'); curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'PayMe-Public-Key: ', ], CURLOPT_POSTFIELDS => $body, ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ## Responses ### 200 — Frictionless approval (`0`) or a challenge (`5`). (success) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `0` frictionless approval: `hash_3dsecure` is returned. `5` challenge: send the buyer to `redirect_url`. | | `hash_3dsecure` | string | The encrypted 3DS result, when `status_code` is `0`. Resolve it with [Resolve a 3D Secure hash](/payments/standalone-3d-secure-service/get-sales-payme-sale-id-3ds-hash). | | `status_message` | string | A message you can show the buyer while redirecting, when `status_code` is `5`. | | `redirect_url` | string | The issuer's challenge page, when `status_code` is `5`. Open it with GET in the buyer's browser. | ```json { "status_code": 5, "status_message": "Generating validation page, please wait", "redirect_url": "https://sandbox.payme.io/3ds/redirect/SALE1700-000000AA-EXAMPLE1-SALE0001?pa=REDACTED" } ``` ### 400 — Browser data is missing, the sale uses another payment method, or 3D Secure was blocked. Codes: `4`, `670`, `720`. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1` on failure. | | `status_error_details` | string | What went wrong, in the language you asked for (Hebrew by default). | | `status_additional_info` | string | The field or value concerned. Can be `null`. | | `status_error_code` | integer | PayMe error code. See the error table above and [Errors](/payments/getting-started/errors). | | `session` | string | PayMe session ID. Quote it when you contact support. | ```json { "status_code": 1, "status_error_details": "Meta data details are missing", "status_additional_info": null, "status_error_code": 670, "session": "a1B2c3D4" } ``` ### 500 — 3D Secure isn't available, the sale wasn't found, or the card data is invalid. See the error table. (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1` on failure. | | `status_error_details` | string | What went wrong, in the language you asked for (Hebrew by default). | | `status_additional_info` | string | The field or value concerned. Can be `null`. | | `status_error_code` | integer | PayMe error code. See the error table above and [Errors](/payments/getting-started/errors). | | `session` | string | PayMe session ID. Quote it when you contact support. | ```json { "status_code": 1, "status_error_details": "Invalid credit card expiry", "status_additional_info": "1221f", "status_error_code": 356, "session": "a1B2c3D4" } ```