# Accept a Card Payment

This guide walks through taking a one-time card payment from start to finish: verify the cardholder with 3D Secure, create the sale and confirm the result. It is the recommended starting point for a new integration.

## Before you start

- You need an API key, provisioned by your SoftLemon admin team and sent on every request as `Authorization: Bearer {YOUR_API_KEY}`.
- All requests in this guide run against the sandbox at `https://api.sandbox.softlemons.com`.
- You need a return URL on your site for the 3D Secure redirect (the `auth_url` below).
- Amounts in requests are in major units, so `12.50` means EUR 12.50. Responses and webhooks report amounts in minor units, so the same value comes back as `1250`.
- Use the sandbox card numbers from the 3D Secure test cards section of the [Merchant API reference](/merchant) to exercise each outcome.

## Step 1: Verify the cardholder with 3D Secure

Start with `POST /api/v1/3ds/verify` because card payments must be authenticated before they are charged. SoftLemon runs a server-managed 3DS flow, so you never handle CAVV, ECI or DS Transaction IDs yourself. The gateway stores them and attaches them to the payment later.

{/* snippet:accept-a-payment:verify-3ds */}
<CodeTabs syncKey="request-lang">

```shell title="cURL"
curl https://api.sandbox.softlemons.com/api/v1/3ds/verify \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer {YOUR_API_KEY}' \
  --data '{
  "amount": 12.5,
  "currency": "EUR",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "auth_url": "https://yoursite.com/checkout/3ds-complete"
}'
```

```js title="JavaScript"
fetch('https://api.sandbox.softlemons.com/api/v1/3ds/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer {YOUR_API_KEY}'
  },
  body: JSON.stringify({
    amount: 12.5,
    currency: 'EUR',
    card: {
      number: '4200000000000091',
      exp_month: 12,
      exp_year: 2030,
      name: 'John Doe',
      cvv: '123'
    },
    auth_url: 'https://yoursite.com/checkout/3ds-complete'
  })
})
```

```python title="Python"
requests.post(
    "https://api.sandbox.softlemons.com/api/v1/3ds/verify",
    headers={
      "Content-Type": "application/json",
      "Authorization": "Bearer {YOUR_API_KEY}"
    },
    json={
      "amount": 12.5,
      "currency": "EUR",
      "card": {
        "number": "4200000000000091",
        "exp_month": 12,
        "exp_year": 2030,
        "name": "John Doe",
        "cvv": "123"
      },
      "auth_url": "https://yoursite.com/checkout/3ds-complete"
    }
)
```

```java title="Java"
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"amount\": 12.5,\n  \"currency\": \"EUR\",\n  \"card\": {\n    \"number\": \"4200000000000091\",\n    \"exp_month\": 12,\n    \"exp_year\": 2030,\n    \"name\": \"John Doe\",\n    \"cvv\": \"123\"\n  },\n  \"auth_url\": \"https://yoursite.com/checkout/3ds-complete\"\n}");
Request request = new Request.Builder()
  .url("https://api.sandbox.softlemons.com/api/v1/3ds/verify")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer {YOUR_API_KEY}")
  .build();

Response response = client.newCall(request).execute();
```

```go title="Go"
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	requestUrl := "https://api.sandbox.softlemons.com/api/v1/3ds/verify"

	payload := strings.NewReader(`{
  "amount": 12.5,
  "currency": "EUR",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "auth_url": "https://yoursite.com/checkout/3ds-complete"
}`)

	req, _ := http.NewRequest("POST", requestUrl, payload)

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Bearer {YOUR_API_KEY}")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```csharp title="C#"
using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://api.sandbox.softlemons.com/api/v1/3ds/verify");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "{YOUR_API_KEY}");
request.Content = new StringContent(
"""
{
  "amount": 12.5,
  "currency": "EUR",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "auth_url": "https://yoursite.com/checkout/3ds-complete"
}
""",
System.Text.Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
```

```kotlin title="Kotlin"
val client = OkHttpClient()

val mediaType = MediaType.parse("application/json")
val body = RequestBody.create(mediaType, "{\n  \"amount\": 12.5,\n  \"currency\": \"EUR\",\n  \"card\": {\n    \"number\": \"4200000000000091\",\n    \"exp_month\": 12,\n    \"exp_year\": 2030,\n    \"name\": \"John Doe\",\n    \"cvv\": \"123\"\n  },\n  \"auth_url\": \"https://yoursite.com/checkout/3ds-complete\"\n}")
val request = Request.Builder()
  .url("https://api.sandbox.softlemons.com/api/v1/3ds/verify")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer {YOUR_API_KEY}")
  .build()

val response = client.newCall(request).execute()
```

```objc title="Objective-C"
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"Content-Type": @"application/json",
                           @"Authorization": @"Bearer {YOUR_API_KEY}" };

NSDictionary *parameters = @{ @"amount": @12.5,
                              @"currency": @"EUR",
                              @"card": @{ @"number": @"4200000000000091", @"exp_month": @12, @"exp_year": @2030, @"name": @"John Doe", @"cvv": @"123" },
                              @"auth_url": @"https://yoursite.com/checkout/3ds-complete" };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.sandbox.softlemons.com/api/v1/3ds/verify"]
                                                      cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                  timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  if (error) {
    NSLog(@"%@", error);
  } else {
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
    NSLog(@"%@", httpResponse);
  }
}];
[dataTask resume];
```

```php title="PHP"
$ch = curl_init("https://api.sandbox.softlemons.com/api/v1/3ds/verify");

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Authorization: Bearer {YOUR_API_KEY}']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
  'amount' => 12.5,
  'currency' => 'EUR',
  'card' => [
    'number' => '4200000000000091',
    'exp_month' => 12,
    'exp_year' => 2030,
    'name' => 'John Doe',
    'cvv' => '123'
  ],
  'auth_url' => 'https://yoursite.com/checkout/3ds-complete'
]));

curl_exec($ch);

curl_close($ch);
```

```ruby title="Ruby"
require 'uri'
require 'net/http'

url = URI("https://api.sandbox.softlemons.com/api/v1/3ds/verify")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request["Authorization"] = 'Bearer {YOUR_API_KEY}'
request.body = <<~JSON
{
  "amount": 12.5,
  "currency": "EUR",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "auth_url": "https://yoursite.com/checkout/3ds-complete"
}
JSON

response = http.request(request)
puts response.read_body
```

```swift title="Swift"
import Foundation

var request = URLRequest(url: URL(string: "https://api.sandbox.softlemons.com/api/v1/3ds/verify")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer {YOUR_API_KEY}", forHTTPHeaderField: "Authorization")
let jsonBody = #"""
{
  "amount": 12.5,
  "currency": "EUR",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "auth_url": "https://yoursite.com/checkout/3ds-complete"
}
"""#
request.httpBody = jsonBody.data(using: .utf8)

let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse,
      200..<300 ~= httpResponse.statusCode else {
  throw URLError(.badServerResponse)
}

print(String(data: data, encoding: .utf8) ?? "")
```

</CodeTabs>
{/* /snippet */}

With the test card `4200000000000091` the issuer does not require a challenge and the response returns immediately:

```json
{
  "success": true,
  "message": "3DS authentication completed (frictionless)",
  "code": "",
  "data": {
    "id": 166,
    "public_id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9",
    "amount": 1250,
    "currency": "EUR",
    "version": "2.2.0",
    "eci": "05",
    "cavv": "AAABA0UREQAAAAAAAAAAAAAAAAA=",
    "status": "full_auth",
    "auth_type": "frictionless",
    "challenge_url": null
  }
}
```

`status: full_auth` means authentication succeeded. Keep `data.public_id` (`tds_01k2h4x9m3n5p7q9r1s3t5v7w9` here). It is the verification's identifier and you will pass it when creating the sale in Step 3. Treat it as an opaque string. The numeric `id` next to it is deprecated and will leave the response on a date announced in the [changelog](/api-basics/changelog); see [conventions](/api-basics/conventions#deprecation).

The immediate response is not always a full authentication. Read `status`, not just the HTTP code:

| `status` | Meaning | What to do |
|---|---|---|
| `full_auth` | The issuer authenticated the cardholder. | Continue to Step 3. |
| `attempt` | The issuer could not fully authenticate the cardholder but returned an attempt proof (ECI 06 or 01). | Continue to Step 3. The attempt proof is forwarded with the payment. |
| `unavailable` | The card is not enrolled for 3DS or authentication could not be performed. No authentication data exists and the `message` says so. | You may still create the sale. It is processed without 3DS and can be soft-declined with `ERR_3DS_REQUIRED` where SCA is mandatory. |
| `failed` | The issuer refused the authentication. Returned as HTTP 400 with code `ERR_3DS_FAILED`. | Do not create the sale. Ask for another card. |

## Step 2: Handle the challenge when the issuer requires one

Some cards trigger a bank challenge instead (test with `4200000000000042`). In that case the response carries a `challenge_url` and no final status yet:

1. Redirect the cardholder to `challenge_url`. The bank runs its own verification there (a code, an app approval or similar).
2. When the cardholder finishes, the gateway redirects them back to your `auth_url` with query parameters: `?card_verification_id={public_id}&status={outcome}`, where `card_verification_id` is the verification's `tds_...` public id and the outcome is one of the statuses in the table above (`full_auth`, `attempt`, `unavailable` or `failed`).
3. On `full_auth` or `attempt`, use the `card_verification_id` value exactly like the `public_id` from the frictionless case, passed through unchanged. On `unavailable`, decide whether to charge without 3DS. On `failed`, show the customer a payment failure and let them try another card.

Your return page should handle every outcome. Nothing has been charged at this point in either flow, and a sale that references a `failed` or unfinished verification is refused with HTTP 422.

## Step 3: Create the sale

Now charge the card with `POST /api/v1/transactions`. Three fields matter beyond the card details:

- `transaction_type: "sale"` charges immediately with no separate capture step. (Use `auth` instead when you want to reserve funds first. See [Authorize now, capture later](/guides/authorize-and-capture).)
- `card_verification_data.id` links the 3DS verification from Step 1 or 2: the `public_id` from the verify response or the `card_verification_id` from the redirect. The stored CAVV, ECI and DS Transaction ID are attached automatically.
- `reference` is your own id for this payment. The gateway allows only one active payment per reference, which protects your customer from double charges. See the [duplicate protection guide](/guides/duplicate-protection).

{/* snippet:accept-a-payment:create-sale */}
<CodeTabs syncKey="request-lang">

```shell title="cURL"
curl https://api.sandbox.softlemons.com/api/v1/transactions \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer {YOUR_API_KEY}' \
  --data '{
  "transaction_type": "sale",
  "amount": 12.5,
  "currency": "EUR",
  "reference": "ORDER-912346",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "card_verification_data": {
    "id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9"
  }
}'
```

```js title="JavaScript"
fetch('https://api.sandbox.softlemons.com/api/v1/transactions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer {YOUR_API_KEY}'
  },
  body: JSON.stringify({
    transaction_type: 'sale',
    amount: 12.5,
    currency: 'EUR',
    reference: 'ORDER-912346',
    card: {
      number: '4200000000000091',
      exp_month: 12,
      exp_year: 2030,
      name: 'John Doe',
      cvv: '123'
    },
    card_verification_data: {
      id: 'tds_01k2h4x9m3n5p7q9r1s3t5v7w9'
    }
  })
})
```

```python title="Python"
requests.post(
    "https://api.sandbox.softlemons.com/api/v1/transactions",
    headers={
      "Content-Type": "application/json",
      "Authorization": "Bearer {YOUR_API_KEY}"
    },
    json={
      "transaction_type": "sale",
      "amount": 12.5,
      "currency": "EUR",
      "reference": "ORDER-912346",
      "card": {
        "number": "4200000000000091",
        "exp_month": 12,
        "exp_year": 2030,
        "name": "John Doe",
        "cvv": "123"
      },
      "card_verification_data": {
        "id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9"
      }
    }
)
```

```java title="Java"
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n  \"transaction_type\": \"sale\",\n  \"amount\": 12.5,\n  \"currency\": \"EUR\",\n  \"reference\": \"ORDER-912346\",\n  \"card\": {\n    \"number\": \"4200000000000091\",\n    \"exp_month\": 12,\n    \"exp_year\": 2030,\n    \"name\": \"John Doe\",\n    \"cvv\": \"123\"\n  },\n  \"card_verification_data\": {\n    \"id\": \"tds_01k2h4x9m3n5p7q9r1s3t5v7w9\"\n  }\n}");
Request request = new Request.Builder()
  .url("https://api.sandbox.softlemons.com/api/v1/transactions")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer {YOUR_API_KEY}")
  .build();

Response response = client.newCall(request).execute();
```

```go title="Go"
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	requestUrl := "https://api.sandbox.softlemons.com/api/v1/transactions"

	payload := strings.NewReader(`{
  "transaction_type": "sale",
  "amount": 12.5,
  "currency": "EUR",
  "reference": "ORDER-912346",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "card_verification_data": {
    "id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9"
  }
}`)

	req, _ := http.NewRequest("POST", requestUrl, payload)

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Bearer {YOUR_API_KEY}")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```csharp title="C#"
using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://api.sandbox.softlemons.com/api/v1/transactions");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "{YOUR_API_KEY}");
request.Content = new StringContent(
"""
{
  "transaction_type": "sale",
  "amount": 12.5,
  "currency": "EUR",
  "reference": "ORDER-912346",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "card_verification_data": {
    "id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9"
  }
}
""",
System.Text.Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
```

```kotlin title="Kotlin"
val client = OkHttpClient()

val mediaType = MediaType.parse("application/json")
val body = RequestBody.create(mediaType, "{\n  \"transaction_type\": \"sale\",\n  \"amount\": 12.5,\n  \"currency\": \"EUR\",\n  \"reference\": \"ORDER-912346\",\n  \"card\": {\n    \"number\": \"4200000000000091\",\n    \"exp_month\": 12,\n    \"exp_year\": 2030,\n    \"name\": \"John Doe\",\n    \"cvv\": \"123\"\n  },\n  \"card_verification_data\": {\n    \"id\": \"tds_01k2h4x9m3n5p7q9r1s3t5v7w9\"\n  }\n}")
val request = Request.Builder()
  .url("https://api.sandbox.softlemons.com/api/v1/transactions")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Authorization", "Bearer {YOUR_API_KEY}")
  .build()

val response = client.newCall(request).execute()
```

```objc title="Objective-C"
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"Content-Type": @"application/json",
                           @"Authorization": @"Bearer {YOUR_API_KEY}" };

NSDictionary *parameters = @{ @"transaction_type": @"sale",
                              @"amount": @12.5,
                              @"currency": @"EUR",
                              @"reference": @"ORDER-912346",
                              @"card": @{ @"number": @"4200000000000091", @"exp_month": @12, @"exp_year": @2030, @"name": @"John Doe", @"cvv": @"123" },
                              @"card_verification_data": @{ @"id": @"tds_01k2h4x9m3n5p7q9r1s3t5v7w9" } };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.sandbox.softlemons.com/api/v1/transactions"]
                                                      cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                  timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  if (error) {
    NSLog(@"%@", error);
  } else {
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
    NSLog(@"%@", httpResponse);
  }
}];
[dataTask resume];
```

```php title="PHP"
$ch = curl_init("https://api.sandbox.softlemons.com/api/v1/transactions");

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Authorization: Bearer {YOUR_API_KEY}']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
  'transaction_type' => 'sale',
  'amount' => 12.5,
  'currency' => 'EUR',
  'reference' => 'ORDER-912346',
  'card' => [
    'number' => '4200000000000091',
    'exp_month' => 12,
    'exp_year' => 2030,
    'name' => 'John Doe',
    'cvv' => '123'
  ],
  'card_verification_data' => [
    'id' => 'tds_01k2h4x9m3n5p7q9r1s3t5v7w9'
  ]
]));

curl_exec($ch);

curl_close($ch);
```

```ruby title="Ruby"
require 'uri'
require 'net/http'

url = URI("https://api.sandbox.softlemons.com/api/v1/transactions")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request["Authorization"] = 'Bearer {YOUR_API_KEY}'
request.body = <<~JSON
{
  "transaction_type": "sale",
  "amount": 12.5,
  "currency": "EUR",
  "reference": "ORDER-912346",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "card_verification_data": {
    "id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9"
  }
}
JSON

response = http.request(request)
puts response.read_body
```

```swift title="Swift"
import Foundation

var request = URLRequest(url: URL(string: "https://api.sandbox.softlemons.com/api/v1/transactions")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer {YOUR_API_KEY}", forHTTPHeaderField: "Authorization")
let jsonBody = #"""
{
  "transaction_type": "sale",
  "amount": 12.5,
  "currency": "EUR",
  "reference": "ORDER-912346",
  "card": {
    "number": "4200000000000091",
    "exp_month": 12,
    "exp_year": 2030,
    "name": "John Doe",
    "cvv": "123"
  },
  "card_verification_data": {
    "id": "tds_01k2h4x9m3n5p7q9r1s3t5v7w9"
  }
}
"""#
request.httpBody = jsonBody.data(using: .utf8)

let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse,
      200..<300 ~= httpResponse.statusCode else {
  throw URLError(.badServerResponse)
}

print(String(data: data, encoding: .utf8) ?? "")
```

</CodeTabs>
{/* /snippet */}

A successful response returns the transaction:

```json
{
  "success": true,
  "message": "Transaction initiated",
  "code": "",
  "data": {
    "id": 2,
    "related_trans_id": null,
    "merchant_id": 1,
    "amount": 1250,
    "currency": "EUR",
    "status": "success",
    "transaction_type": "sale",
    "merchant_trans_id": "ORDER-912346",
    "acquirer_trans_id": "514009741995",
    "acquirer_auth_code": "400066",
    "created_at": "2025-05-20T09:18:47.000000Z"
  }
}
```

Store `data.id`. It is the gateway transaction id you will use for refunds, voids and status checks. See the [transaction statuses guide](/guides/transaction-statuses) for what each `status` value means.

## Step 4: Confirm the result

Treat webhooks as your primary confirmation. When the sale completes, your registered endpoint receives a signed `transaction.succeeded` event within seconds. Verify the signature, acknowledge with a 2xx response and update your order. The [webhook integration guide](/guides/webhooks) covers registration, verification and retries.

For an on-demand answer (for example after downtime or during reconciliation), poll the status endpoint:

{/* snippet:accept-a-payment:poll-status */}
<CodeTabs syncKey="request-lang">

```shell title="cURL"
curl https://api.sandbox.softlemons.com/api/v1/transactions/2/status \
  --header 'Authorization: Bearer {YOUR_API_KEY}'
```

```js title="JavaScript"
fetch('https://api.sandbox.softlemons.com/api/v1/transactions/2/status', {
  headers: {
    Authorization: 'Bearer {YOUR_API_KEY}'
  }
})
```

```python title="Python"
requests.get(
    "https://api.sandbox.softlemons.com/api/v1/transactions/2/status",
    headers={
      "Authorization": "Bearer {YOUR_API_KEY}"
    }
)
```

```java title="Java"
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("https://api.sandbox.softlemons.com/api/v1/transactions/2/status")
  .get()
  .addHeader("Authorization", "Bearer {YOUR_API_KEY}")
  .build();

Response response = client.newCall(request).execute();
```

```go title="Go"
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	requestUrl := "https://api.sandbox.softlemons.com/api/v1/transactions/2/status"

	req, _ := http.NewRequest("GET", requestUrl, nil)

	req.Header.Add("Authorization", "Bearer {YOUR_API_KEY}")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```csharp title="C#"
using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Get, "https://api.sandbox.softlemons.com/api/v1/transactions/2/status");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "{YOUR_API_KEY}");

using var response = await client.SendAsync(request);
```

```kotlin title="Kotlin"
val client = OkHttpClient()

val request = Request.Builder()
  .url("https://api.sandbox.softlemons.com/api/v1/transactions/2/status")
  .get()
  .addHeader("Authorization", "Bearer {YOUR_API_KEY}")
  .build()

val response = client.newCall(request).execute()
```

```objc title="Objective-C"
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"Authorization": @"Bearer {YOUR_API_KEY}" };

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.sandbox.softlemons.com/api/v1/transactions/2/status"]
                                                      cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                  timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  if (error) {
    NSLog(@"%@", error);
  } else {
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
    NSLog(@"%@", httpResponse);
  }
}];
[dataTask resume];
```

```php title="PHP"
$ch = curl_init("https://api.sandbox.softlemons.com/api/v1/transactions/2/status");

curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer {YOUR_API_KEY}']);

curl_exec($ch);

curl_close($ch);
```

```ruby title="Ruby"
require 'uri'
require 'net/http'

url = URI("https://api.sandbox.softlemons.com/api/v1/transactions/2/status")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer {YOUR_API_KEY}'

response = http.request(request)
puts response.read_body
```

```swift title="Swift"
import Foundation

var request = URLRequest(url: URL(string: "https://api.sandbox.softlemons.com/api/v1/transactions/2/status")!)
request.httpMethod = "GET"
request.setValue("Bearer {YOUR_API_KEY}", forHTTPHeaderField: "Authorization")

let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse,
      200..<300 ~= httpResponse.statusCode else {
  throw URLError(.badServerResponse)
}

print(String(data: data, encoding: .utf8) ?? "")
```

</CodeTabs>
{/* /snippet */}

## Errors you should handle

| Response | Meaning | What to do |
|---|---|---|
| `401` | Missing or invalid API key. | Check the `Authorization` header. |
| `409` with `ERR_DUPLICATE` | The `reference` already has an active payment. `data.transaction_id` is the original. | Treat the payment as already made. Do not retry with the same reference. |
| `422` | Validation failed. The response lists the field errors. | Fix the request. Nothing was charged. |
| `400` with `ERR_DO_NOT_RETRY` | The same declined card was retried within the cooldown window. | Wait or ask the customer for another card. |
| `400` with `ERR_3DS_REQUIRED` | The sale was sent without `card_verification_data` and the issuer requires authentication. The transaction is recorded as `failed` with `status_reason` `3ds_required`; `data.transaction` is that row and `data.next_action` points at `POST /api/v1/3ds/verify`. | Go back to Step 1, run the verification, then create a new sale with `card_verification_data.id`. The refused sale cannot be continued. |
| `422` with `ERR_3DS_REQUIRED` or `ERR_3DS_FAILED` | The `card_verification_data.id` you sent belongs to a verification that has not finished, or that ended in `failed`. | Wait for the outcome, or run a new verification. Nothing was charged. |
