المدفوعات

العملاء

سجّل من يدفع، واربط مدفوعاته به، واعثر عليه من جديد ببريده الإلكتروني.

فتح بصيغة Markdown
في هذه الصفحة
  1. أنشئ عميلًا
  2. اربط دفعة بعميل
  3. اعثر على عميل ببريده الإلكتروني
  4. كل طلبات العملاء
  5. في لوحة التحكم
  6. اجمع ما تحتاجه فقط

العميل يسجّل من يدفع لك: بريد إلكتروني واسم ورقم هاتف ومراجعك الخاصة. هو اختياري: تعمل الدفعة من دونه. ومعه، تجمع لوحة التحكم مدفوعات العميل في صفحة واحدة، وتعثر عليه من جديد ببريده الإلكتروني.

ينتمي العميل إلى مؤسسة واحدة ووضع واحد. العميل الذي يُنشأ بمفتاح اختبار لا يوجد إلا في وضع الاختبار.

أنشئ عميلًا

أرسل ما تعرفه؛ كل الحقول اختيارية:

  • email؛
  • name؛
  • phone، بالصيغة الدولية مع رمز البلد: +212612345678؛
  • metadata: مراجعك الخاصة، مثل رقم حساب هذا الشخص لديك. انظر البيانات الوصفية.

تُقبل ترويسة Idempotency-Key لكنها ليست إلزامية. ومعها، الطلب الذي يُرسل مرتين ينشئ عميلًا واحدًا.

الاستجابة هي العميل، مع معرّفه (cus_…). احفظه مع سجلك الخاص بهذا الشخص.

  • curl
  • Node.js
  • TypeScript
  • Python
  • PHP
  • Ruby
  • Go
  • Java
  • C#
  • Rust
customer.mjscustomer.tscustomer.pycustomer.phpcustomer.rbcustomer.goCustomer.javaCustomer.csmain.rs
curl https://pay-api.adpromax.com/v1/customers \
  -u "$APX_SECRET_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "name": "Salma Idrissi",
    "phone": "+212612345678",
    "metadata": { "account_id": "8841" }
  }'
const response = await fetch('https://pay-api.adpromax.com/v1/customers', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: '[email protected]',
    name: 'Salma Idrissi',
    phone: '+212612345678',
    metadata: { account_id: '8841' },
  }),
})
const customer = await response.json()
if (!response.ok)
  throw new Error(customer.error.message)
console.log(customer.id)
interface Customer {
  id: string
  object: 'customer'
  email: string | null
  name: string | null
  phone: 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/customers', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: '[email protected]',
    name: 'Salma Idrissi',
    phone: '+212612345678',
    metadata: { account_id: '8841' },
  }),
})
if (!response.ok) {
  const { error } = (await response.json()) as ApiError
  throw new Error(`${error.code}: ${error.message}`)
}
const customer = (await response.json()) as Customer
console.log(customer.id)
import os

import requests

response = requests.post(
    "https://pay-api.adpromax.com/v1/customers",
    auth=(os.environ["APX_SECRET_KEY"], ""),
    json={
        "email": "[email protected]",
        "name": "Salma Idrissi",
        "phone": "+212612345678",
        "metadata": {"account_id": "8841"},
    },
)
customer = response.json()
response.raise_for_status()
print(customer["id"])
<?php
$ch = curl_init('https://pay-api.adpromax.com/v1/customers');
curl_setopt_array($ch, [
    CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'email' => '[email protected]',
        'name' => 'Salma Idrissi',
        'phone' => '+212612345678',
        'metadata' => ['account_id' => '8841'],
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$customer = json_decode(curl_exec($ch), true);
echo $customer['id'], PHP_EOL;
require 'json'
require 'net/http'

uri = URI('https://pay-api.adpromax.com/v1/customers')
request = Net::HTTP::Post.new(uri)
request.basic_auth(ENV.fetch('APX_SECRET_KEY'), '')
request['Content-Type'] = 'application/json'
request.body = JSON.generate(
  email: '[email protected]',
  name: 'Salma Idrissi',
  phone: '+212612345678',
  metadata: { account_id: '8841' }
)

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

puts customer['id']
package main

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

type Customer struct {
	ID    string `json:"id"`
	Email string `json:"email"`
	Name  string `json:"name"`
}

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

func main() {
	body, err := json.Marshal(map[string]any{
		"email":    "[email protected]",
		"name":     "Salma Idrissi",
		"phone":    "+212612345678",
		"metadata": map[string]string{"account_id": "8841"},
	})
	if err != nil {
		log.Fatal(err)
	}
	req, err := http.NewRequest(http.MethodPost,
		"https://pay-api.adpromax.com/v1/customers", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}
	req.SetBasicAuth(os.Getenv("APX_SECRET_KEY"), "")
	req.Header.Set("Content-Type", "application/json")

	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 customer Customer
	if err := json.NewDecoder(res.Body).Decode(&customer); err != nil {
		log.Fatal(err)
	}
	fmt.Println(customer.ID)
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Customer {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "email": "[email protected]",
              "name": "Salma Idrissi",
              "phone": "+212612345678",
              "metadata": { "account_id": "8841" }
            }
            """;
        HttpRequest request = HttpRequest.newBuilder(
                URI.create("https://pay-api.adpromax.com/v1/customers"))
            .header("Authorization", "Bearer " + System.getenv("APX_SECRET_KEY"))
            .header("Content-Type", "application/json")
            .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 customer as JSON: read its id with a JSON library.
        System.out.println(response.body());
    }
}
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 response = await http.PostAsJsonAsync("https://pay-api.adpromax.com/v1/customers", new
{
    email = "[email protected]",
    name = "Salma Idrissi",
    phone = "+212612345678",
    metadata = new { account_id = "8841" },
});
if (!response.IsSuccessStatusCode)
{
    var failure = await response.Content.ReadFromJsonAsync<ApiError>(json);
    throw new InvalidOperationException(failure?.Error.Message);
}
var customer = await response.Content.ReadFromJsonAsync<Customer>(json);
Console.WriteLine(customer!.Id);

record Customer(string Id, string? Email, string? Name, string? Phone);
record ApiError(ApiErrorBody Error);
record ApiErrorBody(string Code, string Message);
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
//             serde_json = "1"
use serde::Deserialize;
use serde_json::json;

#[derive(Deserialize)]
struct Customer {
    id: String,
}

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

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::blocking::Client::new()
        .post("https://pay-api.adpromax.com/v1/customers")
        .bearer_auth(std::env::var("APX_SECRET_KEY")?)
        .json(&json!({
            "email": "[email protected]",
            "name": "Salma Idrissi",
            "phone": "+212612345678",
            "metadata": { "account_id": "8841" }
        }))
        .send()?;
    if !response.status().is_success() {
        let failure: ApiError = response.json()?;
        return Err(failure.error.message.into());
    }
    let customer: Customer = response.json()?;
    println!("{}", customer.id);
    Ok(())
}
الاستجابة200 OK
{
  "id": "cus_01k7e3m9q2v5b8n1x4c7z0r6tw",
  "object": "customer",
  "email": "[email protected]",
  "name": "Salma Idrissi",
  "phone": "+212612345678",
  "metadata": { "account_id": "8841" },
  "created": 1790855000,
  "livemode": false
}

اربط دفعة بعميل

مرّر معرّف العميل في customer عند إنشاء نية دفع (payment intent) أو جلسة دفع (checkout session). تظهر الدفعة عندئذ في صفحة العميل في لوحة التحكم، وتذكر نية الدفع عميلها.

يجب أن ينتمي العميل إلى مؤسستك وإلى وضع الدفعة نفسه. يُرفض أي معرّف آخر بـ 400، resource_missing، مع param بقيمة customer: العميل الذي أُنشئ بمفتاح اختبار لا يستطيع الدفع في الوضع المباشر.

لعرض مدفوعات عميل، صفِّ نيات الدفع: GET /v1/payment_intents?customer=cus_….

اعثر على عميل ببريده الإلكتروني

يعرض GET /v1/customers عملاءك، الأحدث أولًا؛ ومع email، أصحاب ذلك العنوان فقط. قبل أن تنشئ عميلًا، ابحث عنه: فتضم سجلاتك عميلًا واحدًا لكل شخص.

تجيب القائمة بكائن قائمة: يحمل data العملاء، ويوضح has_more هل يتبعهم آخرون. اطلب حتى 100 في المرة الواحدة عبر limit (10 افتراضيًا)، ثم اطلب التالين عبر starting_after بقيمة آخر معرّف تلقيته؛ ويسير ending_before في الاتجاه المعاكس.

  • curl
  • Node.js
  • TypeScript
  • Python
  • PHP
  • Ruby
  • Go
  • Java
  • C#
  • Rust
find-customer.mjsfind-customer.tsfind_customer.pyfind_customer.phpfind_customer.rbfind_customer.goFindCustomer.javaFindCustomer.csmain.rs
EMAIL=[email protected]
CUS=$(curl -s -G https://pay-api.adpromax.com/v1/customers \
  -u "$APX_SECRET_KEY:" \
  --data-urlencode "email=$EMAIL" \
  -d limit=1 | jq -r '.data[0].id // empty')

if [ -z "$CUS" ]; then
  CUS=$(curl -s https://pay-api.adpromax.com/v1/customers \
    -u "$APX_SECRET_KEY:" \
    -H "Content-Type: application/json" \
    -d "{\"email\": \"$EMAIL\", \"name\": \"Salma Idrissi\"}" | jq -r .id)
fi
echo "$CUS"
const api = 'https://pay-api.adpromax.com/v1'
const headers = {
  'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
  'Content-Type': 'application/json',
}
const email = '[email protected]'

const query = new URLSearchParams({ email, limit: '1' })
const found = await fetch(`${api}/customers?${query}`, { headers })
  .then(response => response.json())
let customer = found.data[0]
if (customer === undefined) {
  const response = await fetch(`${api}/customers`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ email, name: 'Salma Idrissi' }),
  })
  customer = await response.json()
}
console.log(customer.id)
interface Customer {
  id: string
  object: 'customer'
  email: string | null
  name: string | null
}

interface CustomerList {
  object: 'list'
  data: Customer[]
  has_more: boolean
}

const api = 'https://pay-api.adpromax.com/v1'
const headers = {
  'Authorization': `Bearer ${process.env.APX_SECRET_KEY}`,
  'Content-Type': 'application/json',
}
const email = '[email protected]'

const query = new URLSearchParams({ email, limit: '1' })
const found = await fetch(`${api}/customers?${query}`, { headers })
  .then(response => response.json() as Promise<CustomerList>)
let customer: Customer | undefined = found.data[0]
if (customer === undefined) {
  const response = await fetch(`${api}/customers`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ email, name: 'Salma Idrissi' }),
  })
  customer = (await response.json()) as Customer
}
console.log(customer.id)
import os

import requests

API = "https://pay-api.adpromax.com/v1"
AUTH = (os.environ["APX_SECRET_KEY"], "")
email = "[email protected]"

found = requests.get(
    f"{API}/customers", auth=AUTH, params={"email": email, "limit": 1}
).json()
customer = found["data"][0] if found["data"] else None
if customer is None:
    customer = requests.post(
        f"{API}/customers", auth=AUTH,
        json={"email": email, "name": "Salma Idrissi"},
    ).json()
print(customer["id"])
<?php
function api(string $method, string $path, ?array $body = null): array
{
    $ch = curl_init('https://pay-api.adpromax.com/v1' . $path);
    curl_setopt_array($ch, [
        CURLOPT_USERPWD => getenv('APX_SECRET_KEY') . ':',
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    if ($body !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
    }
    $answer = json_decode(curl_exec($ch), true);
    return $answer;
}

$email = '[email protected]';
$query = http_build_query(['email' => $email, 'limit' => 1]);
$customer = api('GET', "/customers?{$query}")['data'][0] ?? null;
if ($customer === null) {
    $customer = api('POST', '/customers', [
        'email' => $email,
        'name' => 'Salma Idrissi',
    ]);
}
echo $customer['id'], PHP_EOL;
require 'json'
require 'net/http'

API = 'https://pay-api.adpromax.com/v1'
email = '[email protected]'

def call(request)
  uri = request.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
  JSON.parse(response.body)
end

uri = URI("#{API}/customers")
uri.query = URI.encode_www_form(email: email, limit: 1)
customer = call(Net::HTTP::Get.new(uri))['data'].first
if customer.nil?
  request = Net::HTTP::Post.new(URI("#{API}/customers"))
  request['Content-Type'] = 'application/json'
  request.body = JSON.generate(email: email, name: 'Salma Idrissi')
  customer = call(request)
end
puts customer['id']
package main

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

const api = "https://pay-api.adpromax.com/v1"

type Customer struct {
	ID    string `json:"id"`
	Email string `json:"email"`
}

type CustomerList struct {
	Data []Customer `json:"data"`
}

func call(method, path string, body io.Reader, into any) error {
	req, err := http.NewRequest(method, api+path, body)
	if err != nil {
		return err
	}
	req.SetBasicAuth(os.Getenv("APX_SECRET_KEY"), "")
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	return json.NewDecoder(res.Body).Decode(into)
}

func main() {
	email := "[email protected]"
	query := url.Values{"email": {email}, "limit": {"1"}}

	var found CustomerList
	path := "/customers?" + query.Encode()
	if err := call(http.MethodGet, path, nil, &found); err != nil {
		log.Fatal(err)
	}
	var customer Customer
	if len(found.Data) > 0 {
		customer = found.Data[0]
	} else {
		body, _ := json.Marshal(map[string]string{
			"email": email,
			"name":  "Salma Idrissi",
		})
		err := call(http.MethodPost, "/customers", bytes.NewReader(body), &customer)
		if err != nil {
			log.Fatal(err)
		}
	}
	fmt.Println(customer.ID)
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;

public class FindCustomer {
    static final String API = "https://pay-api.adpromax.com/v1";
    static final HttpClient HTTP = HttpClient.newHttpClient();
    // The JDK has no JSON parser: a regex reads a customer's id here.
    static final Pattern ID = Pattern.compile("\"id\"\\s*:\\s*\"(cus_[^\"]+)\"");

    static String send(HttpRequest.Builder request) throws Exception {
        request.header("Authorization", "Bearer " + System.getenv("APX_SECRET_KEY"));
        request.header("Content-Type", "application/json");
        return HTTP.send(request.build(), HttpResponse.BodyHandlers.ofString()).body();
    }

    public static void main(String[] args) throws Exception {
        String email = "[email protected]";
        String query = "email=" + URLEncoder.encode(email, StandardCharsets.UTF_8);
        String found = send(HttpRequest.newBuilder(
            URI.create(API + "/customers?" + query + "&limit=1")).GET());
        var id = ID.matcher(found);
        if (!id.find()) {
            String created = send(HttpRequest.newBuilder(URI.create(API + "/customers"))
                .POST(HttpRequest.BodyPublishers.ofString("""
                    {"email": "%s", "name": "Salma Idrissi"}
                    """.formatted(email))));
            id = ID.matcher(created);
            if (!id.find()) {
                throw new IllegalStateException(created);
            }
        }
        System.out.println(id.group(1));
    }
}
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 { BaseAddress = new Uri("https://pay-api.adpromax.com/v1/") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", Environment.GetEnvironmentVariable("APX_SECRET_KEY"));

var email = "[email protected]";
var found = await http.GetFromJsonAsync<CustomerList>(
    $"customers?email={Uri.EscapeDataString(email)}&limit=1", json);
var customer = found!.Data.FirstOrDefault();
if (customer is null)
{
    using var response = await http.PostAsJsonAsync(
        "customers", new { email, name = "Salma Idrissi" });
    customer = await response.Content.ReadFromJsonAsync<Customer>(json);
}
Console.WriteLine(customer!.Id);

record CustomerList(List<Customer> Data, bool HasMore);
record Customer(string Id, string? Email, string? Name);
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
//             serde = { version = "1", features = ["derive"] }
//             serde_json = "1"
use reqwest::blocking::Client;
use serde::Deserialize;
use serde_json::json;

const API: &str = "https://pay-api.adpromax.com/v1";

#[derive(Deserialize)]
struct Customer {
    id: String,
}

#[derive(Deserialize)]
struct CustomerList {
    data: Vec<Customer>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new();
    let key = std::env::var("APX_SECRET_KEY")?;
    let email = "[email protected]";

    let found: CustomerList = client
        .get(format!("{API}/customers"))
        .bearer_auth(&key)
        .query(&[("email", email), ("limit", "1")])
        .send()?
        .json()?;
    let first = found.data.into_iter().next();
    let customer = match first {
        Some(customer) => customer,
        None => client
            .post(format!("{API}/customers"))
            .bearer_auth(&key)
            .json(&json!({ "email": email, "name": "Salma Idrissi" }))
            .send()?
            .json()?,
    };
    println!("{}", customer.id);
    Ok(())
}
الاستجابة200 OK
{
  "object": "list",
  "data": [
    {
      "id": "cus_01k7e3m9q2v5b8n1x4c7z0r6tw",
      "object": "customer",
      "email": "[email protected]",
      "name": "Salma Idrissi",
      "phone": "+212612345678",
      "metadata": { "account_id": "8841" },
      "created": 1790855000,
      "livemode": false
    }
  ],
  "has_more": false,
  "url": "/v1/customers"
}

كل طلبات العملاء

الطلبما يفعله
POST /v1/customersينشئ عميلًا.
GET /v1/customers/{id}يجلب عميلًا.
POST /v1/customers/{id}يحدّث عميلًا: أرسل الحقول التي تتغير.
DELETE /v1/customers/{id}يحذف عميلًا. الاستجابة هي { "id": "cus_…", "object": "customer", "deleted": true }.
GET /v1/customersيعرض عملاءك، الأحدث أولًا؛ ويُبقي email أصحاب ذلك العنوان.
GET /v1/payment_intents?customer=cus_…يعرض مدفوعات عميل.

في لوحة التحكم

  • تعرض العملاء قائمتهم مع البريد الإلكتروني والهاتف وعدد المدفوعات ومجموع المدفوع وآخر دفعة، ومعها مربع بحث.
  • ينشئ عميل جديد عميلًا يدويًا: الاسم والبريد الإلكتروني والهاتف والبيانات الوصفية.
  • تعرض صفحة العميل مدفوعاته وبياناته الوصفية. عدّل التفاصيل في مكانها، في الصفحة.

اجمع ما تحتاجه فقط

يحمل العميل بيانات شخصية. لا تطلب من عملائك إلا ما يحتاجه نشاطك التجاري، وأبقِ الأسرار خارج metadata: يستطيع فريقك كله ونقاط استقبال الـ webhook لديك قراءتها.