# صفحة الدفع المستضافة

صفحة دفع نستضيفها للبطاقة والنقد، بألوانك. كيف تنشئ جلسة دفع، وما يراه عميلك، وكيف تنتهي الجلسة.

صفحة الدفع المستضافة (hosted checkout) صفحة دفع يستضيفها AdProMaxPay لك. ينشئ خادمك **جلسة
دفع** (checkout session)، ويدفع عميلك في الصفحة بالبطاقة أو نقدًا، ويعرف خادمك النتيجة
من webhook. لا تتعامل مع رقم بطاقة أبدًا، وتعمل الجلسة نفسها مع الطريقتين.

الصفحة:

* تعرض شعارك واسم نشاطك التجاري والمبلغ وجهة اتصال الدعم لديك؛
* تتحدث الفرنسية والإنجليزية والعربية: تتبع متصفح عميلك، وتعود إلى الفرنسية إذا
  تعذّر عليها تحديد اللغة، وفيها مبدّل للغة؛
* مصممة للهواتف أولًا، وزر *ادفع* في المتناول دائمًا؛
* تحمل شريطًا مخططًا **TEST** في وضع الاختبار، حتى لا يخلط أحد بين صفحة اختبار وصفحة حقيقية.

لتجربتها من البداية إلى النهاية في عشر دقائق، اتبع [البدء السريع للبطاقة](https://pay-docs.adpromax.com/ar/get-started/quickstart-card).

## حياة الجلسة

حالات جلسة الدفع:

- `open` → `complete`: دُفعت
- `open` → `expired`: انتهت دون دفع، أو أنهيتها أنت

تكون الجلسة `open` منذ إنشائها. تصبح `complete` عند نجاح نية الدفع (payment intent) الخاصة بها،
و`expired` عند بلوغها `expires_at` دون دفع، أو عندما تنهي صلاحيتها أنت. لكل جلسة نية دفع
واحدة، يسميها الحقل `payment_intent`.

## أنشئ جلسة

أنشئ جلسة واحدة لكل طلبية يوشك عميلك أن يدفعها، عبر
`POST /v1/checkout/sessions`. ترويسة `Idempotency-Key` إلزامية: انظر
[عدم التكرار](https://pay-docs.adpromax.com/ar/api-reference/idempotency) (idempotency).

| المعامل                | ما يحمله                                                                                                  |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| `amount`               | المبلغ، عدد صحيح بالسنتيم: `45000` تساوي 450.00 MAD.                                                      |
| `currency`             | دائمًا `mad`.                                                                                             |
| `payment_method_types` | الطرق التي يمكن لعميلك اختيارها: `card` أو `cash` أو كلتاهما. يُعرض الدفع نقدًا ابتداءً من 100.00 MAD.    |
| `success_url`          | العنوان الذي ترسل إليه الصفحة عميلك بعد الدفع. بـ `https`؛ ويُقبل `http://localhost` في وضع الاختبار فقط. |
| `cancel_url`           | العنوان الذي يرسل إليه *رجوع* عميلك قبل الدفع. القاعدة نفسها.                                             |
| `expires_at`           | اختياري. وقت انتهاء الجلسة، بثواني Unix، بين 30 دقيقة و24 ساعة من الآن. 24 ساعة افتراضيًا.                |
| `customer`             | اختياري. [عميل](https://pay-docs.adpromax.com/ar/payments/customers) (`cus_…`) من المؤسسة نفسها والوضع نفسه: تظهر الدفعة في سجله.         |
| `metadata`             | اختياري. مراجعك الخاصة، مثل رقم الطلبية: انظر [البيانات الوصفية](https://pay-docs.adpromax.com/ar/payments/metadata).                     |

الاستجابة هي الجلسة: `id` و`url` و`payment_intent` و`status` الخاصة بها. احفظ
معرّف نية الدفع مع طلبيتك: تذكره الأحداث التي تتلقاها.

```bash
curl https://pay-api.adpromax.com/v1/checkout/sessions \
  -u "$APX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: checkout-order-2087" \
  -d '{
    "amount": 45000,
    "currency": "mad",
    "payment_method_types": ["card", "cash"],
    "success_url": "https://shop.example.com/orders/2087/thanks",
    "cancel_url": "https://shop.example.com/cart",
    "expires_at": '"$(($(date +%s) + 7200))"',
    "customer": "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
    "metadata": { "order_id": "2087" }
  }'
```

```js title="session.mjs"
const response = await fetch('https://pay-api.adpromax.com/v1/checkout/sessions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'checkout-order-2087',
  },
  body: JSON.stringify({
    amount: 45000,
    currency: 'mad',
    payment_method_types: ['card', 'cash'],
    success_url: 'https://shop.example.com/orders/2087/thanks',
    cancel_url: 'https://shop.example.com/cart',
    // Two hours from now, in Unix seconds.
    expires_at: Math.floor(Date.now() / 1000) + 2 * 60 * 60,
    customer: 'cus_01k7e2f9g3h6j8k2m5n7p4q1sd',
    metadata: { order_id: '2087' },
  }),
})
const session = await response.json()
if (!response.ok)
  throw new Error(session.error.message)
console.log(session.payment_intent, session.url)
```

```ts title="session.ts"
interface CheckoutSession {
  id: string
  object: 'checkout.session'
  amount: number
  currency: string
  payment_intent: string
  status: string
  url: string
  expires_at: number
  customer: string | null
  metadata: Record<string, string>
}

interface ApiError {
  error: { type: string, code: string, message: string, request_id: string }
}

const response = await fetch('https://pay-api.adpromax.com/v1/checkout/sessions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'checkout-order-2087',
  },
  body: JSON.stringify({
    amount: 45000,
    currency: 'mad',
    payment_method_types: ['card', 'cash'],
    success_url: 'https://shop.example.com/orders/2087/thanks',
    cancel_url: 'https://shop.example.com/cart',
    // Two hours from now, in Unix seconds.
    expires_at: Math.floor(Date.now() / 1000) + 2 * 60 * 60,
    customer: 'cus_01k7e2f9g3h6j8k2m5n7p4q1sd',
    metadata: { order_id: '2087' },
  }),
})
if (!response.ok) {
  const { error } = (await response.json()) as ApiError
  throw new Error(`${error.code}: ${error.message}`)
}
const session = (await response.json()) as CheckoutSession
console.log(session.payment_intent, session.url)
```

```python title="session.py"
import os
import time

import requests

response = requests.post(
    "https://pay-api.adpromax.com/v1/checkout/sessions",
    auth=(os.environ["APX_SECRET_KEY"], ""),
    headers={"Idempotency-Key": "checkout-order-2087"},
    json={
        "amount": 45000,
        "currency": "mad",
        "payment_method_types": ["card", "cash"],
        "success_url": "https://shop.example.com/orders/2087/thanks",
        "cancel_url": "https://shop.example.com/cart",
        # Two hours from now, in Unix seconds.
        "expires_at": int(time.time()) + 2 * 60 * 60,
        "customer": "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
        "metadata": {"order_id": "2087"},
    },
)
session = response.json()
response.raise_for_status()
print(session["payment_intent"], session["url"])
```

```php title="session.php"
<?php
$ch = curl_init('https://pay-api.adpromax.com/v1/checkout/sessions');
curl_setopt_array($ch, [
    CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Idempotency-Key: checkout-order-2087',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 45000,
        'currency' => 'mad',
        'payment_method_types' => ['card', 'cash'],
        'success_url' => 'https://shop.example.com/orders/2087/thanks',
        'cancel_url' => 'https://shop.example.com/cart',
        // Two hours from now, in Unix seconds.
        'expires_at' => time() + 2 * 60 * 60,
        'customer' => 'cus_01k7e2f9g3h6j8k2m5n7p4q1sd',
        'metadata' => ['order_id' => '2087'],
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$session = json_decode(curl_exec($ch), true);
echo $session['payment_intent'], ' ', $session['url'], PHP_EOL;
```

```ruby title="session.rb"
require 'json'
require 'net/http'

uri = URI('https://pay-api.adpromax.com/v1/checkout/sessions')
request = Net::HTTP::Post.new(uri)
request.basic_auth(ENV.fetch('APX_SECRET_KEY'), '')
request['Content-Type'] = 'application/json'
request['Idempotency-Key'] = 'checkout-order-2087'
request.body = JSON.generate(
  amount: 45000,
  currency: 'mad',
  payment_method_types: %w[card cash],
  success_url: 'https://shop.example.com/orders/2087/thanks',
  cancel_url: 'https://shop.example.com/cart',
  # Two hours from now, in Unix seconds.
  expires_at: Time.now.to_i + 2 * 60 * 60,
  customer: 'cus_01k7e2f9g3h6j8k2m5n7p4q1sd',
  metadata: { order_id: '2087' }
)

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(request)
end
session = JSON.parse(response.body)
raise session.dig('error', 'message') unless response.is_a?(Net::HTTPSuccess)

puts "#{session['payment_intent']} #{session['url']}"
```

```go title="session.go"
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"
)

type CheckoutSession struct {
	ID            string `json:"id"`
	PaymentIntent string `json:"payment_intent"`
	Status        string `json:"status"`
	URL           string `json:"url"`
	ExpiresAt     int64  `json:"expires_at"`
}

type APIError struct {
	Error struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

func main() {
	// Two hours from now, in Unix seconds.
	expiresAt := time.Now().Add(2 * time.Hour).Unix()
	body, err := json.Marshal(map[string]any{
		"amount":               45000,
		"currency":             "mad",
		"payment_method_types": []string{"card", "cash"},
		"success_url":          "https://shop.example.com/orders/2087/thanks",
		"cancel_url":           "https://shop.example.com/cart",
		"expires_at":           expiresAt,
		"customer":             "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
		"metadata":             map[string]string{"order_id": "2087"},
	})
	if err != nil {
		log.Fatal(err)
	}
	req, err := http.NewRequest(http.MethodPost,
		"https://pay-api.adpromax.com/v1/checkout/sessions", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}
	req.SetBasicAuth(os.Getenv("APX_SECRET_KEY"), "")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Idempotency-Key", "checkout-order-2087")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		var apiErr APIError
		json.NewDecoder(res.Body).Decode(&apiErr)
		log.Fatal(apiErr.Error.Message)
	}
	var session CheckoutSession
	if err := json.NewDecoder(res.Body).Decode(&session); err != nil {
		log.Fatal(err)
	}
	fmt.Println(session.PaymentIntent, session.URL)
}
```

```java title="Session.java"
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;

public class Session {
    public static void main(String[] args) throws Exception {
        // Two hours from now, in Unix seconds.
        long expiresAt = Instant.now().getEpochSecond() + 2 * 60 * 60;
        String body = """
            {
              "amount": 45000,
              "currency": "mad",
              "payment_method_types": ["card", "cash"],
              "success_url": "https://shop.example.com/orders/2087/thanks",
              "cancel_url": "https://shop.example.com/cart",
              "expires_at": %d,
              "customer": "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
              "metadata": { "order_id": "2087" }
            }
            """.formatted(expiresAt);
        HttpRequest request = HttpRequest.newBuilder(
                URI.create("https://pay-api.adpromax.com/v1/checkout/sessions"))
            .header("Authorization", "Bearer " + System.getenv("APX_SECRET_KEY"))
            .header("Content-Type", "application/json")
            .header("Idempotency-Key", "checkout-order-2087")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new IllegalStateException(response.body());
        }
        // The session as JSON: read payment_intent and url with a JSON library.
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```

```csharp title="Session.cs"
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var json = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("APX_SECRET_KEY"));

using var request = new HttpRequestMessage(
    HttpMethod.Post, "https://pay-api.adpromax.com/v1/checkout/sessions");
request.Headers.Add("Idempotency-Key", "checkout-order-2087");
request.Content = JsonContent.Create(new
{
    amount = 45000,
    currency = "mad",
    payment_method_types = new[] { "card", "cash" },
    success_url = "https://shop.example.com/orders/2087/thanks",
    cancel_url = "https://shop.example.com/cart",
    // Two hours from now, in Unix seconds.
    expires_at = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 2 * 60 * 60,
    customer = "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
    metadata = new { order_id = "2087" },
});

using var response = await http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
    var failure = await response.Content.ReadFromJsonAsync<ApiError>(json);
    throw new InvalidOperationException(failure?.Error.Message);
}
var session = await response.Content.ReadFromJsonAsync<CheckoutSession>(json);
Console.WriteLine($"{session!.PaymentIntent} {session.Url}");

record CheckoutSession(string Id, string PaymentIntent, string Status, string Url);
record ApiError(ApiErrorBody Error);
record ApiErrorBody(string Code, string Message);
```

```rust title="main.rs"
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
//             serde_json = "1"
use std::time::{SystemTime, UNIX_EPOCH};

use serde::Deserialize;
use serde_json::json;

#[derive(Deserialize)]
struct CheckoutSession {
    payment_intent: String,
    url: String,
}

#[derive(Deserialize)]
struct ApiError {
    error: ErrorBody,
}

#[derive(Deserialize)]
struct ErrorBody {
    message: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Two hours from now, in Unix seconds.
    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
    let expires_at = now + 2 * 60 * 60;

    let response = reqwest::blocking::Client::new()
        .post("https://pay-api.adpromax.com/v1/checkout/sessions")
        .bearer_auth(std::env::var("APX_SECRET_KEY")?)
        .header("Idempotency-Key", "checkout-order-2087")
        .json(&json!({
            "amount": 45000,
            "currency": "mad",
            "payment_method_types": ["card", "cash"],
            "success_url": "https://shop.example.com/orders/2087/thanks",
            "cancel_url": "https://shop.example.com/cart",
            "expires_at": expires_at,
            "customer": "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
            "metadata": { "order_id": "2087" }
        }))
        .send()?;
    if !response.status().is_success() {
        let failure: ApiError = response.json()?;
        return Err(failure.error.message.into());
    }
    let session: CheckoutSession = response.json()?;
    println!("{} {}", session.payment_intent, session.url);
    Ok(())
}
```

```json title="200 OK" response
{
  "id": "cs_01k7e3m5q8w2r6t9y4b7n1v5ca",
  "amount": 45000,
  "currency": "mad",
  "payment_method_types": ["card", "cash"],
  "payment_intent": "pi_01k7e3m5px4z8c2v6b9n3m7hra",
  "customer": "cus_01k7e2f9g3h6j8k2m5n7p4q1sd",
  "status": "open",
  "url": "https://pay.example.com/c/cs_01k7e3m5q8w2r6t9y4b7n1v5ca#…",
  "success_url": "https://shop.example.com/orders/2087/thanks",
  "cancel_url": "https://shop.example.com/cart",
  "expires_at": 1790854200,
  "metadata": { "order_id": "2087" },
  "livemode": false
}
```

## أرسل عميلك إلى الصفحة

أعد توجيه متصفح عميلك إلى `url` الجلسة برمز HTTP `303`، كما في
[البدء السريع](https://pay-docs.adpromax.com/ar/get-started/quickstart-card#3-أرسل-عميلك-إلى-الصفحة).

احتفظ بالعنوان كاملًا، بما فيه الجزء الذي يلي `#`. هذا الجزء هو وسيلة وصول عميلك إلى هذه
الجلسة: لا ترسله المتصفحات إلى أي خادم أبدًا، فيبقى خارج السجلات. لا تسجّله ولا تختصره ولا
ترسله إلى أحد غير عميلك.

## ما يراه عميلك

أولًا، المبلغ والاختيار بين الطرق التي عرضتها.

**بالبطاقة:**

1. يختار عميلك البطاقة ويضغط *ادفع*. تجهّز الصفحة الدفعة، ثم توضح أنها
   ستنقله إلى صفحة البطاقة الآمنة لشريكنا في البطاقات. في وضع الاختبار، تلك الصفحة هي
   محاكاتنا التي تحمل علامة TEST، ولا تقبل إلا [بطاقات الاختبار](https://pay-docs.adpromax.com/ar/sandbox/test-cards).
2. يكتب بيانات بطاقته في تلك الصفحة. وإذا طلب بنكه 3-D Secure، يؤكد هناك.
3. يعود إلى صفحتنا، فتعرض «جارٍ التأكيد مع بنكك…» إلى أن تتأكد
   الدفعة، ثم صفحة النجاح.
4. بعد 5 ثوانٍ، ترسله صفحة النجاح إلى `success_url`.

إذا رُفضت البطاقة، تذكر الصفحة السبب وتعرض *جرّب بطاقة أخرى*، أو *ادفع نقدًا بدلًا من
ذلك* إن كنت عرضت الدفع نقدًا وكان المبلغ 100.00 MAD على الأقل. انظر
[الدفع بالبطاقة](https://pay-docs.adpromax.com/ar/payments/card-payments).

**نقدًا:** يدخل عميلك اسمه الكامل ومدينته (رقم الهاتف اختياري)، ثم
يحصل على قسيمة فيها رمز دفع يسدده لدى وكيل دفع شريك. انظر
[الدفع نقدًا](https://pay-docs.adpromax.com/ar/payments/cash-payments).

في الطريقتين، يمكن لعميلك إدخال عنوان بريد إلكتروني لتلقي إيصال؛ ولا يُرسل الإيصال إلا
إذا أدخله.

رابط *رجوع* في الصفحة، وهو يذكر اسم نشاطك التجاري، يرسل عميلك إلى `cancel_url`. تبقى
الجلسة `open` إلى أن تنتهي صلاحيتها، فيظل `url` نفسه يعمل إن عاد.

## اعرف متى تم الدفع

نفّذ الطلبية عند أحد هذه الأحداث، لا عند العودة إلى `success_url` أبدًا:

| الحدث                        | متى                                                   |
| ---------------------------- | ----------------------------------------------------- |
| `payment_intent.succeeded`   | نجحت نية الدفع الخاصة بالجلسة.                        |
| `checkout.session.completed` | دُفعت الجلسة: ويذكر الحدث `payment_intent` الخاص بها. |
| `checkout.session.expired`   | انتهت الجلسة دون دفع. حرّر الطلبية أو المخزون.        |

تتلقى أيضًا الأحداث الأخرى لنية الدفع، مثل `payment_intent.payment_failed` بعد
رفض بطاقة. اقرأ [إعادة التوجيه وإشعارات webhook](https://pay-docs.adpromax.com/ar/payments/how-payments-work#إعادة-التوجيه-وإشعارات-webhook-بأيهما-تثق)
لتعرف لماذا لا تثبت العودة شيئًا.

رمز الدفع النقدي الذي حصل عليه عميلك يحتفظ بأجله الخاص، نحو ثلاثة أيام، حتى لو حلّ
`expires_at` الجلسة قبله: يستطيع سداده مع ذلك.

## أنهِ صلاحية جلسة

إذا أُلغيت الطلبية أو نفد المخزون، فأنهِ صلاحية الجلسة المفتوحة بنفسك عبر
`POST /v1/checkout/sessions/{id}/expire`. تصبح الجلسة `expired` وتُلغى نية الدفع غير المدفوعة
الخاصة بها: لا يستطيع عميلك دفعها بعد ذلك. لا يمكن إنهاء صلاحية جلسة إلا إذا كانت `open`.

لقراءة جلسة من جديد، استخدم `GET /v1/checkout/sessions/{id}`؛ ولعرض قائمة الجلسات،
`GET /v1/checkout/sessions`.

```bash
curl -X POST \
  https://pay-api.adpromax.com/v1/checkout/sessions/cs_01k7e3m5q8w2r6t9y4b7n1v5ca/expire \
  -u "$APX_SECRET_KEY:"
```

```js title="expire.mjs"
const id = 'cs_01k7e3m5q8w2r6t9y4b7n1v5ca'
const response = await fetch(`https://pay-api.adpromax.com/v1/checkout/sessions/${id}/expire`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.APX_SECRET_KEY}` },
})
const session = await response.json()
console.log(session.status) // expired
```

```ts title="expire.ts"
interface CheckoutSession {
  id: string
  object: 'checkout.session'
  status: string
}

const id = 'cs_01k7e3m5q8w2r6t9y4b7n1v5ca'
const response = await fetch(`https://pay-api.adpromax.com/v1/checkout/sessions/${id}/expire`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.APX_SECRET_KEY}` },
})
const session = (await response.json()) as CheckoutSession
console.log(session.status) // expired
```

```python title="expire.py"
import os

import requests

session_id = "cs_01k7e3m5q8w2r6t9y4b7n1v5ca"
response = requests.post(
    f"https://pay-api.adpromax.com/v1/checkout/sessions/{session_id}/expire",
    auth=(os.environ["APX_SECRET_KEY"], ""),
)
print(response.json()["status"])  # expired
```

```php title="expire.php"
<?php
$id = 'cs_01k7e3m5q8w2r6t9y4b7n1v5ca';
$ch = curl_init("https://pay-api.adpromax.com/v1/checkout/sessions/{$id}/expire");
curl_setopt_array($ch, [
    CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
]);
$session = json_decode(curl_exec($ch), true);
echo $session['status'], PHP_EOL; // expired
```

```ruby title="expire.rb"
require 'json'
require 'net/http'

id = 'cs_01k7e3m5q8w2r6t9y4b7n1v5ca'
uri = URI("https://pay-api.adpromax.com/v1/checkout/sessions/#{id}/expire")
request = Net::HTTP::Post.new(uri)
request.basic_auth(ENV.fetch('APX_SECRET_KEY'), '')

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(request)
end
puts JSON.parse(response.body)['status'] # expired
```

```go title="expire.go"
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

type CheckoutSession struct {
	ID     string `json:"id"`
	Status string `json:"status"`
}

func main() {
	id := "cs_01k7e3m5q8w2r6t9y4b7n1v5ca"
	req, err := http.NewRequest(http.MethodPost,
		"https://pay-api.adpromax.com/v1/checkout/sessions/"+id+"/expire", nil)
	if err != nil {
		log.Fatal(err)
	}
	req.SetBasicAuth(os.Getenv("APX_SECRET_KEY"), "")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	var session CheckoutSession
	if err := json.NewDecoder(res.Body).Decode(&session); err != nil {
		log.Fatal(err)
	}
	fmt.Println(session.Status) // expired
}
```

```java title="Expire.java"
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Expire {
    public static void main(String[] args) throws Exception {
        String id = "cs_01k7e3m5q8w2r6t9y4b7n1v5ca";
        HttpRequest request = HttpRequest.newBuilder(
                URI.create("https://pay-api.adpromax.com/v1/checkout/sessions/" + id + "/expire"))
            .header("Authorization", "Bearer " + System.getenv("APX_SECRET_KEY"))
            .POST(HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        // The session as JSON: its status is now "expired".
        System.out.println(response.body());
    }
}
```

```csharp title="Expire.cs"
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var json = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("APX_SECRET_KEY"));

var id = "cs_01k7e3m5q8w2r6t9y4b7n1v5ca";
using var response = await http.PostAsync(
    $"https://pay-api.adpromax.com/v1/checkout/sessions/{id}/expire", null);
var session = await response.Content.ReadFromJsonAsync<CheckoutSession>(json);
Console.WriteLine(session!.Status); // expired

record CheckoutSession(string Id, string Status);
```

```rust title="main.rs"
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
use serde::Deserialize;

#[derive(Deserialize)]
struct CheckoutSession {
    status: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let id = "cs_01k7e3m5q8w2r6t9y4b7n1v5ca";
    let session: CheckoutSession = reqwest::blocking::Client::new()
        .post(format!("https://pay-api.adpromax.com/v1/checkout/sessions/{id}/expire"))
        .bearer_auth(std::env::var("APX_SECRET_KEY")?)
        .send()?
        .json()?;
    println!("{}", session.status); // expired
    Ok(())
}
```

## علامتك التجارية

اضبط مظهر الصفحة مرة واحدة، من لوحة التحكم في **الإعدادات ← العلامة التجارية**:

* شعار بصيغة PNG أو JPEG أو WebP، حتى 512 KB؛
* الاسم المعروض وبريد الدعم الإلكتروني وهاتفه؛
* لون للزر: إذا كان النص الأبيض صعب القراءة عليه، يُغمَّق اللون حتى يصبح مقروءًا،
  وتخبرك صفحة الإعدادات بذلك؛
* خلفية بيضاء، أو خلفية ملوّنة تلوينًا خفيفًا بلونك.

تستخدم صفحة القسيمة في الدفع نقدًا الإعدادات نفسها.

## بلا كود إطلاقًا

**رابط الدفع**، الذي يُنشأ من لوحة التحكم، يفتح الصفحة المستضافة نفسها لكل من لديه الرابط.
انظر [بلا كود: رابط دفع](https://pay-docs.adpromax.com/ar/get-started/quickstart-card#بلا-كود-رابط-دفع).
