# 

## When to use a one-step sale

* **Instant delivery** — digital goods, downloads, or services fulfilled immediately
* **Point of sale** — in-person transactions where the item is handed to the customer
* **Simple checkout** — any scenario where you do not need a separate capture step


If you need to hold funds before charging, use [Authorize and capture](/docs/tutorials/payment-cookbook/authorize-and-capture) instead.

## Flow

![image](https://files.modern-mermaid.live/images/1776276832818-mermaid-diagram-1776276832914.png)

## Prerequisites

* A valid API key — see [Obtain API credentials](/docs/how-tos/setup/obtain-credentials)
* A TSYS integration configured for card processing


## Step 1: Send the sale request

Set `type` to `card_sale` and include the card details. The amount is in the smallest currency unit (cents for USD).

curl
```shell
curl -X POST \
  https://api.dev.paradisegateway.net/v1/transactions \
  -H "Content-Type: application/json" \
  -H "APIKEY: YOUR-API-KEY" \
  -d '{
    "type": "card_sale",
    "amount": 1999,
    "card": {
      "pan": "4111111111111111",
      "expiry_month": "12",
      "expiry_year": "2027",
      "cvv": "123",
      "cardholder_name": "John Doe",
      "address": { "line1": "456 Oak Ave", "zip": "90210" }
    }
  }'
```

Python
```python
import os, requests

API_KEY = os.environ["PARADISE_API_KEY"]
BASE = os.environ.get("PARADISE_BASE_URL", "https://api.dev.paradisegateway.net")
HEADERS = {"Content-Type": "application/json", "APIKEY": API_KEY}

resp = requests.post(f"{BASE}/v1/transactions", headers=HEADERS, json={
    "type": "card_sale",
    "amount": 1999,
    "card": {
        "pan": "4111111111111111",
        "expiry_month": "12",
        "expiry_year": "2027",
        "cvv": "123",
        "cardholder_name": "John Doe",
        "address": {"line1": "456 Oak Ave", "zip": "90210"},
    },
})
txn = resp.json()
print(f"Transaction {txn['id']}: {txn['response_status']}")
```

JavaScript
```javascript
const API_KEY = process.env.PARADISE_API_KEY;
const BASE = process.env.PARADISE_BASE_URL || "https://api.dev.paradisegateway.net";
const headers = { "Content-Type": "application/json", "APIKEY": API_KEY };

const resp = await fetch(`${BASE}/v1/transactions`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    type: "card_sale",
    amount: 1999,
    card: {
      pan: "4111111111111111",
      expiry_month: "12",
      expiry_year: "2027",
      cvv: "123",
      cardholder_name: "John Doe",
      address: { line1: "456 Oak Ave", zip: "90210" },
    },
  }),
});
const txn = await resp.json();
console.log(`Transaction ${txn.id}: ${txn.response_status}`);
```

C#
```csharp
using var client = new HttpClient();
var baseUrl = Environment.GetEnvironmentVariable("PARADISE_BASE_URL") ?? "https://api.dev.paradisegateway.net";
client.DefaultRequestHeaders.Add("APIKEY", Environment.GetEnvironmentVariable("PARADISE_API_KEY"));

var resp = await client.PostAsJsonAsync($"{baseUrl}/v1/transactions", new {
    type = "card_sale",
    amount = 1999,
    card = new {
        pan = "4111111111111111",
        expiry_month = "12",
        expiry_year = "2027",
        cvv = "123",
        cardholder_name = "John Doe",
        address = new { line1 = "456 Oak Ave", zip = "90210" },
    },
});
Console.WriteLine(await resp.Content.ReadAsStringAsync());
```

Java
```java
HttpClient client = HttpClient.newHttpClient();
String baseUrl = System.getenv().getOrDefault("PARADISE_BASE_URL", "https://api.dev.paradisegateway.net");

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(baseUrl + "/v1/transactions"))
    .header("Content-Type", "application/json")
    .header("APIKEY", System.getenv("PARADISE_API_KEY"))
    .POST(HttpRequest.BodyPublishers.ofString("""
        {
          "type": "card_sale",
          "amount": 1999,
          "card": {
            "pan": "4111111111111111",
            "expiry_month": "12",
            "expiry_year": "2027",
            "cvv": "123",
            "cardholder_name": "John Doe",
            "address": { "line1": "456 Oak Ave", "zip": "90210" }
          }
        }"""))
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```

Go
```go
payload := `{
  "type": "card_sale",
  "amount": 1999,
  "card": {
    "pan": "4111111111111111",
    "expiry_month": "12",
    "expiry_year": "2027",
    "cvv": "123",
    "cardholder_name": "John Doe",
    "address": {"line1": "456 Oak Ave", "zip": "90210"}
  }
}`
req, _ := http.NewRequest("POST", base+"/v1/transactions", strings.NewReader(payload))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("APIKEY", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
```

PHP
```php
$payload = json_encode([
    "type" => "card_sale",
    "amount" => 1999,
    "card" => [
        "pan" => "4111111111111111",
        "expiry_month" => "12",
        "expiry_year" => "2027",
        "cvv" => "123",
        "cardholder_name" => "John Doe",
        "address" => ["line1" => "456 Oak Ave", "zip" => "90210"],
    ],
]);
$ch = curl_init("{$base}/v1/transactions");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json", "APIKEY: {$apiKey}"],
    CURLOPT_POSTFIELDS => $payload,
]);
echo curl_exec($ch);
```

Ruby
```ruby
uri = URI("#{base}/v1/transactions")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["APIKEY"] = api_key
req.body = { type: "card_sale", amount: 1999, card: {
  pan: "4111111111111111", expiry_month: "12", expiry_year: "2027",
  cvv: "123", cardholder_name: "John Doe",
  address: { line1: "456 Oak Ave", zip: "90210" }
} }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.body
```

## Step 2: Handle the response

```json
{
  "id": "TRANSACTION-01KEW33R7ZOW22U44YHES8UHXD",
  "type": "card_sale",
  "amount": 1999,
  "transaction_state": "captured",
  "response_status": "approved",
  "response_code": "00",
  "response_message": "Approved",
  "avs_result_code": "Y",
  "cvv_result": "M",
  "correlation_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}
```

### Check the result

| Field | Value | Meaning |
|  --- | --- | --- |
| `response_status` | `approved` | Payment succeeded |
| `response_status` | `declined` | Issuer refused — ask for a different card |
| `response_status` | `error` | Gateway/processor issue — retry with backoff |
| `avs_result_code` | `Y` | Address and ZIP match |
| `cvv_result` | `M` | CVV matches |


```python
if txn["response_status"] == "approved":
    print(f"Payment approved — ID: {txn['id']}")
elif txn["response_status"] == "declined":
    print(f"Declined: {txn['response_message']} — ask for a different card")
else:
    print(f"Error: {txn['response_message']} — retry or escalate")
```

## Using a stored token

If the customer has a stored payment method, pass `payment_token` instead of raw card data.

```shell
curl -X POST \
  https://api.dev.paradisegateway.net/v1/transactions \
  -H "Content-Type: application/json" \
  -H "APIKEY: YOUR-API-KEY" \
  -d '{
    "type": "card_sale",
    "amount": 1999,
    "payment_token": "PAYMENTTOKEN-01JQXYZ123ABC456DEF789GHI"
  }'
```

See [Process a payment with a stored token](/docs/how-tos/process-payments/process-payment-with-token) for details.

## Best practices

* **Always check `response_status`** — do not assume success from HTTP 200 alone. A `200` with `response_status: declined` is a decline.
* **Log `correlation_id`** — include it in your records for debugging and support.
* **Include AVS data** — send `address.line1` and `address.zip` to improve approval rates.
* **Use idempotency keys** — include an `Idempotency-Key` header on retries to prevent duplicate charges.
* **Validate before sending** — Luhn-check the card number, verify expiry is in the future, ensure amount is positive.


## Further reading

* [Process a credit card sale (how-to)](/docs/how-tos/process-payments/process-credit-card)
* [Handle declined transactions](/docs/tutorials/payment-cookbook/handle-declined-transactions)
* [Handle payment failures](/docs/tutorials/payment-cookbook/handle-payment-failures)
* [Sale vs. authorization](/docs/concepts/transactions/sale-vs-auth)