Saltar al contenido
Qualth

Registrá una compra y leé el estado resultante

Registrá una `purchase` con idempotencia, interpretá su resultado y verificá el estado posterior del `customer`.

Antes de empezar

  • Necesitás un customer.id ya resuelto y el program_id correspondiente.
  • POST /api/purchases requiere purchases.write.
  • GET /api/customers/{id}/state requiere customers.read.
  • Generá una Idempotency-Key estable por cada intento lógico de compra.

1. Registrá la compra

El body público de POST /api/purchases usa exactamente customer_id, program_id, total_amount, currency_code y external_reference. Enviá además Idempotency-Key: no puede quedar vacía después de trim y su longitud máxima es 128 caracteres.

2. Interpretá la respuesta

Una respuesta HTTP 200 puede incluir transactionId, pointsEarned, balance, previousTier, currentTier, tierChanged, idempotentReplay y externalReference. transactionId identifica la operación resultante; puntos, balance y tier describen el efecto de loyalty expuesto por la compra.

3. Reintentá sin duplicar efectos

  • Misma Idempotency-Key + mismo payload → HTTP 200, misma transacción y idempotentReplay = true.
  • Misma key + payload diferente → HTTP 409 idempotency_conflict.
  • idempotency_in_progress → repetí únicamente el mismo request con la misma key.
  • purchase_reconciliation_required → preservá request y key, y reconciliá antes de iniciar otra compra.

4. Leé el estado resultante

Llamá a GET /api/customers/{id}/state. La lectura expone customer.id, programId, points, currentTier, active y recentOperation; cuando existe, recentOperation.transactionId permite correlacionar la operación reciente.

Compra, transacción y estado son conceptos distintos

  • purchase es el evento comercial que tu integración envía a Qualth.
  • transactionId identifica la operación persistida que devuelve el contrato de purchase.
  • El estado del customer es la lectura posterior que tu integración puede usar para verificar el resultado observable.

Ejemplos probados

Los requests y replays de esta guía se renderizan desde ejemplos validados contra el contrato pinneado de Integration API.

TestedrecordPurchase

purchase-record

HTTP esperado
200
Última validación
2026-08-11
Evidencia
deterministic-ci+controlled-smoke

The public purchase request uses exactly customer_id, program_id, total_amount, currency_code and external_reference plus Idempotency-Key.

cURL

curl --request POST \
  --url 'https://api.qualth.com/api/purchases' \
  --header 'X-API-Key: {{api_key}}' \
  --header 'X-Tenant-Subdomain: {{tenant_subdomain}}' \
  --header 'Idempotency-Key: {{new_idempotency_key}}' \
  --header 'Content-Type: application/json' \
  --data '{
  "customer_id": {{new_customer_id}},
  "program_id": {{program_id}},
  "total_amount": {{purchase_amount}},
  "currency_code": "ARS",
  "external_reference": "{{new_external_reference}}"
}'

JavaScript

const response = await fetch("https://api.qualth.com/api/purchases", {
  method: "POST",
  headers: {
    "X-API-Key": "{{api_key}}",
    "X-Tenant-Subdomain": "{{tenant_subdomain}}",
    "Idempotency-Key": "{{new_idempotency_key}}",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "customer_id": Number("{{new_customer_id}}"),
  "program_id": Number("{{program_id}}"),
  "total_amount": Number("{{purchase_amount}}"),
  "currency_code": "ARS",
  "external_reference": "{{new_external_reference}}"
}),
})
const data = await response.json()
TestedrecordPurchase

purchase-identical-replay

HTTP esperado
200
Última validación
2026-08-11
Evidencia
deterministic-ci+controlled-smoke

Identical replay returns the same transaction and must not duplicate points, balance or transaction effects.

cURL

curl --request POST \
  --url 'https://api.qualth.com/api/purchases' \
  --header 'X-API-Key: {{api_key}}' \
  --header 'X-Tenant-Subdomain: {{tenant_subdomain}}' \
  --header 'Idempotency-Key: {{new_idempotency_key}}' \
  --header 'Content-Type: application/json' \
  --data '{
  "customer_id": {{new_customer_id}},
  "program_id": {{program_id}},
  "total_amount": {{purchase_amount}},
  "currency_code": "ARS",
  "external_reference": "{{new_external_reference}}"
}'

JavaScript

const response = await fetch("https://api.qualth.com/api/purchases", {
  method: "POST",
  headers: {
    "X-API-Key": "{{api_key}}",
    "X-Tenant-Subdomain": "{{tenant_subdomain}}",
    "Idempotency-Key": "{{new_idempotency_key}}",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "customer_id": Number("{{new_customer_id}}"),
  "program_id": Number("{{program_id}}"),
  "total_amount": Number("{{purchase_amount}}"),
  "currency_code": "ARS",
  "external_reference": "{{new_external_reference}}"
}),
})
const data = await response.json()
TestedrecordPurchase

purchase-idempotency-conflict

HTTP esperado
409
Última validación
2026-08-11
Evidencia
deterministic-ci+controlled-smoke

Reusing an Idempotency-Key with a different payload is an explicit 409 idempotency_conflict.

cURL

curl --request POST \
  --url 'https://api.qualth.com/api/purchases' \
  --header 'X-API-Key: {{api_key}}' \
  --header 'X-Tenant-Subdomain: {{tenant_subdomain}}' \
  --header 'Idempotency-Key: {{new_idempotency_key}}' \
  --header 'Content-Type: application/json' \
  --data '{
  "customer_id": {{new_customer_id}},
  "program_id": {{program_id}},
  "total_amount": 1600,
  "currency_code": "ARS",
  "external_reference": "{{new_external_reference}}"
}'

JavaScript

const response = await fetch("https://api.qualth.com/api/purchases", {
  method: "POST",
  headers: {
    "X-API-Key": "{{api_key}}",
    "X-Tenant-Subdomain": "{{tenant_subdomain}}",
    "Idempotency-Key": "{{new_idempotency_key}}",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "customer_id": Number("{{new_customer_id}}"),
  "program_id": Number("{{program_id}}"),
  "total_amount": 1600,
  "currency_code": "ARS",
  "external_reference": "{{new_external_reference}}"
}),
})
const data = await response.json()
TestedgetCustomerState

customer-state-after-purchase

HTTP esperado
200
Última validación
2026-08-11
Evidencia
deterministic-ci+controlled-smoke

State is sanitized, contains no customer profile email and reflects the most recent purchase transaction.

cURL

curl --request GET \
  --url 'https://api.qualth.com/api/customers/{{new_customer_id}}/state' \
  --header 'X-API-Key: {{api_key}}' \
  --header 'X-Tenant-Subdomain: {{tenant_subdomain}}'

JavaScript

const response = await fetch("https://api.qualth.com/api/customers/{{new_customer_id}}/state", {
  method: "GET",
  headers: {
    "X-API-Key": "{{api_key}}",
    "X-Tenant-Subdomain": "{{tenant_subdomain}}"
  }
})
const data = await response.json()