Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.lux-core.io/llms.txt

Use this file to discover all available pages before exploring further.

Payments Guide

This guide covers everything you need to know about creating and managing payments through the LuxCore API.

Payment Types

LuxCore supports four payment types:
TypeDirectionDescription
depositInboundCustomer pays to merchant (checkout)
withdrawalOutboundMerchant pays to customer/vendor (payout)
deposit_ppInboundDeposit with hosted payment page
withdrawal_ppOutboundWithdrawal with hosted payment page

Payment Methods

MethodCodeDepositWithdrawalProcessing Time
SPEIspeiUp to 30 min
OXXOoxxoUp to 30 min
CardcardUp to 30 min
Bank Transferbank_transferUp to 30 min
CashcashUp to 30 min
CryptocryptoUp to 30 min
PayIDpayidUp to 10 min
iPayipayInstant (redirect)
All amounts are in minor units (centavos, cents). For example, to charge 1,000.00 ARS, pass amount: 100000. Passing 1000 would create a payment for only 10.00 ARS.

Creating a Deposit (Customer Payment)

When a customer needs to pay you:
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "deposit",
    "merchant_reference": "order_abc123",
    "description": "Order #ABC123",
    "customer": {
      "name": "Maria Garcia",
      "email": "maria@example.com",
      "phone": "+5491112345678"
    },
    "metadata": {
      "order_id": "abc123",
      "source": "web"
    }
  }'

Response (ARS)

{
  "transaction_id": "pay_1234567890_abcdefgh",
  "status": "processing",
  "amount": 150000,
  "currency": "ARS",
  "method": "bank_transfer",
  "type": "deposit",
  "bank_details": {
    "amount": "150000",
    "purpose": "Payment pay_1234567890_abcdefgh",
    "currency": "ARS",
    "bank_name": "Banco Nacion",
    "swift_code": null,
    "account_holder": "LuxCore Platform S.A.",
    "account_number": "0110012345678901234567"
  },
  "created_at": "2025-01-21T10:30:00Z"
}

Creating a Withdrawal (Payout)

To send money to a beneficiary:
customer vs payout: For withdrawals, both objects refer to the same person — the recipient of the funds.
  • customer — personal contact information (name, email, phone). Used for customer identification, matching, and communication.
  • payout — bank account details for executing the transfer (account number, bank code, reference).

Payout Fields

FieldRequiredDescriptionExample
recipient_nameYesFull name of the beneficiary (must match bank account holder)"Juan Perez Martinez"
bank_accountYesBank account number. CBU/CVU (22 digits) for ARS, CLABE (18 digits) for MXN, account number for UYU, account number (6-10 digits) for AUD"0110012345678901234567"
bank_codeNoBank code. Required for MXN/SPEI. For AUD, use BSB (6 digits, format XXX-XXX)"002"
account_typeNoAccount type"checking"
beneficiary_tax_idNoBeneficiary tax ID (RFC, RUC, CUIT, ABN)"20-12345678-9"
descriptionNoPurpose of the payout"Salary January 2025"
referenceNoMerchant-provided tracking reference. If not provided, system generates one automatically"PAYOUT-JAN-001"
payidNoPayID identifier (email, phone, or ABN). Required when method is payid for AUD withdrawals"james@example.com"
payid_typeNoType of PayID: email, phone, or abn. Required when payid is provided"email"
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 250000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "withdrawal",
    "merchant_reference": "payout_vendor_001",
    "description": "Payout January 2025",
    "customer": {
      "name": "Juan Perez Martinez",
      "email": "juan.perez@example.com",
      "phone": "+5491112345678"
    },
    "payout": {
      "recipient_name": "Juan Perez Martinez",
      "bank_account": "0110012345678901234567"
    }
  }'

Response (ARS Withdrawal)

{
  "transaction_id": "pay_1234567890_abcdefgh",
  "status": "pending",
  "amount": 250000,
  "currency": "ARS",
  "method": "bank_transfer",
  "type": "withdrawal",
  "merchant_reference": "payout_vendor_001",
  "payout": {
    "recipient_name": "Juan Perez Martinez",
    "bank_account": "0110012345678901234567",
    "status": "pending"
  },
  "created_at": "2025-01-21T10:30:00Z"
}
Withdrawals require sufficient balance in your merchant account. Use the Balance API to check available funds before initiating payouts.

Per-Payment Webhook URL

You can optionally specify a webhook_url to receive events for a specific payment without pre-configuring a webhook endpoint:
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "deposit",
    "merchant_reference": "order_abc123",
    "customer": {
      "name": "Maria Garcia",
      "email": "maria@example.com"
    },
    "webhook_url": "https://your-server.com/webhooks/payments",
    "webhook_events": ["payment.completed", "payment.failed"]
  }'
FieldTypeRequiredDescription
webhook_urlstringNoHTTPS URL to receive webhook events for this payment (max 2048 characters)
webhook_eventsstring[]NoEvents to subscribe to. Defaults to ["payment.created", "payment.completed", "payment.failed", "payment.refunded"]
In-request webhooks are signed using your merchant’s default webhook secret. See the Webhooks Guide for details on signature verification and secret management.

Traffic Type

Optionally tag payments with a traffic type for analytics and routing:
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "deposit",
    "merchant_reference": "order_123",
    "traffic_type": "primary",
    "customer": {
      "name": "Customer Name",
      "email": "customer@example.com"
    }
  }'
ValueDescription
primaryPrimary traffic (default routing)
secondarySecondary/backup traffic
vipVIP/priority traffic
traffic_type is optional. If not provided, the payment uses default routing. Traffic type is included in payment responses and CSV exports.

Hosted Payment Pages

For a simpler integration, use hosted payment pages (deposit_pp or withdrawal_pp):
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "deposit_pp",
    "merchant_reference": "checkout_123",
    "customer": {
      "name": "Customer Name",
      "email": "customer@example.com"
    }
  }'
Response includes a payment page URL:
{
  "transaction_id": "pay_xyz789",
  "status": "processing",
  "payment_page": {
    "url": "https://{payment-page-host}/pay/{token}?sig={signature}&exp={expires}"
  }
}
The payment page URL is dynamically generated and may vary based on the selected currency, payment method, and region. Always use the exact URL returned in the API response.
Redirect your customer to this URL to complete the payment.

Payment Statuses

Understanding payment statuses is crucial for proper integration. Each status represents a specific state in the payment lifecycle.

Status Reference

StatusDescriptionTerminalWebhooks Sent
createdPayment created, not yet assigned to a provider-
pending_approvalPayment awaiting manual approval-
pendingPayment created, awaiting processing or approval-
processingPayment is being processed, awaiting customer action-
completedPayment was successful, funds transferredpayment.completed
failedPayment failed due to error or rejectionpayment.failed
cancelledPayment was cancelled by merchant or systempayment.cancelled
expiredPayment expired before completionpayment.failed
refundedPayment was fully refundedpayment.refunded
partial_refundPayment was partially refundedpayment.refunded
Terminal statuses (completed, failed, cancelled, expired, refunded, partial_refund) are final states - the payment cannot transition to non-terminal status.

Initial Status by Payment Type

Payment TypeInitial StatusDescription
depositprocessingCustomer needs to complete the transfer
withdrawalpendingAwaiting approval and processing
deposit_ppprocessingCustomer redirected to payment page
withdrawal_pppendingAwaiting approval

Status Descriptions

The payment has been created in the system but has not yet been assigned to a payment provider or requisite. This is a transient status that typically transitions quickly to pending or processing.Next possible statuses: pending, processing, failed
The payment requires manual approval before it can be processed. This may apply to high-value transactions or flagged payments that need review.Next possible statuses: processing, cancelled
The payment has been created and is waiting to be processed. For withdrawals, this means the payout is queued and awaiting approval or processing by the payment provider.Next possible statuses: processing, cancelled, expired
The payment is actively being processed. For deposits, this means the customer has been given payment instructions (bank details, payment page URL) and we are waiting for them to complete the transfer.Next possible statuses: completed, failed, cancelled, expired
The payment was successful. For deposits, the funds have been received and credited to your merchant balance. For withdrawals, the funds have been sent to the beneficiary.This is a terminal status. You will receive a payment.completed webhook.
The payment could not be completed. This can happen due to various reasons: bank rejection, invalid account details, insufficient funds (for withdrawals), or technical issues.This is a terminal status. You will receive a payment.failed webhook with an error_code and error_message.
The payment was cancelled before completion. This can be initiated by the merchant via API, by an admin, or automatically by the system.This is a terminal status. You will receive a payment.cancelled webhook.
The payment was not completed within the allowed time window. Deposits typically expire after 1-60 minutes (configurable per merchant).This is a terminal status. You will receive a payment.failed webhook with error_code: TTL_EXPIRED.
The payment was fully refunded after completion. The refunded amount has been deducted from your merchant balance.This is a terminal status. You will receive a payment.refunded webhook.
The payment was partially refunded. Part of the original amount has been returned to the customer.This is a terminal status. You will receive a payment.refunded webhook with the refunded amount.

Initiating Refunds

Refunds are currently initiated through the support team. A programmatic Refunds API is planned for Q2 2026.
To request a refund:
  1. Create a support ticket with category refund_request via the Tickets API or the merchant dashboard
  2. Include the payment_id of the payment to be refunded
  3. The support team will process the refund and update the payment status
  4. You will receive a payment.refunded webhook when the refund is complete
Refund amounts are deducted from your merchant balance (available_amount decreases).

Handling Status Changes

Always use webhooks to track payment status changes. Polling the API is not recommended as it adds latency and may hit rate limits.
// Example: Handling status in your webhook handler
switch (payment.status) {
  case 'completed':
    // Update order to paid, deliver goods/services
    await markOrderAsPaid(payment.merchant_reference);
    break;
  case 'failed':
  case 'expired':
    // Notify customer, allow retry
    await notifyPaymentFailed(payment.merchant_reference, payment.error_message);
    break;
  case 'cancelled':
    // Clean up pending order
    await cancelOrder(payment.merchant_reference);
    break;
  case 'refunded':
  case 'partial_refund':
    // Process refund in your system
    await processRefund(payment.merchant_reference, payment.refunded_amount);
    break;
}

Retrieving Payment Status

curl -X GET "https://api.lux-core.io/api/v1/payments/pay_1234567890_abcdefgh" \
  -H "X-API-Key: qp_test_sk_your_key"

Listing Payments

curl -X GET "https://api.lux-core.io/api/v1/payments?status=completed&limit=20" \
  -H "X-API-Key: qp_test_sk_your_key"

Query Parameters

ParameterTypeDescription
statusstringFilter by status
methodstringFilter by payment method
created_at_fromISO 8601Start date filter
created_at_toISO 8601End date filter
limitintegerResults per page (max 100)
offsetintegerPagination offset

Customer Management

Payments are automatically linked to customers in your account. You can optionally pass an existing customer ID to link a payment to a specific customer.

Customer Fields

FieldTypeRequiredDescription
namestringNoCustomer full name (max 255 characters)
emailstringNoCustomer email address (max 255 characters)
phonestringNoCustomer phone number (max 50 characters)
external_idstringNoYour unique customer identifier from your system. Used to link payments to your internal customer records
All customer fields are optional. However, providing at least email or phone is recommended for automatic customer matching and communication.
external_id is your own customer reference (e.g., user ID from your database). It is stored on the customer record and is unique per merchant. Use it for reconciliation and analytics without relying on LuxCore internal IDs.

Automatic Customer Matching

When creating a payment, if you don’t provide a customer_id, the system will:
  1. Search for an existing customer by email (exact match, case-insensitive)
  2. If not found by email, search by phone (normalized)
  3. If no match found, create a new customer with the provided data
If external_id is provided and a customer is found by email or phone, the external_id will be assigned to the existing customer (if not already set).

Using External ID

Pass external_id inside the customer object to link payments to your internal customer records:
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "deposit",
    "merchant_reference": "order_abc123",
    "customer": {
      "name": "Maria Garcia",
      "email": "maria@example.com",
      "external_id": "usr_98765"
    }
  }'
external_id must be unique per merchant. If you attempt to create a new customer with an external_id that is already assigned to another customer within the same merchant account, the request will fail.

Using Customer ID

If you have an existing customer, you can pass their ID directly:
curl -X POST "https://api.lux-core.io/api/v1/payments" \
  -H "X-API-Key: qp_test_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "currency": "ARS",
    "method": "bank_transfer",
    "type": "deposit",
    "merchant_reference": "order_abc123",
    "customer_id": 12345,
    "customer": {
      "name": "Maria Garcia",
      "email": "maria@example.com"
    }
  }'
When customer_id is provided, the system validates that the customer exists and belongs to your merchant account. The customer object fields (name, email, phone, external_id) are not updated in this case — only the link to the existing customer is used.

Customer in Response

Payment responses now include customer information:
{
  "transaction_id": "pay_1234567890_abcdefgh",
  "status": "processing",
  "customer_id": 12345,
  "customer": {
    "id": 12345,
    "name": "Maria Garcia",
    "email": "maria@example.com",
    "phone": "+5491112345678",
    "external_id": "usr_98765"
  }
}

Cancelling a Payment

Cancel a pending payment:
curl -X POST "https://api.lux-core.io/api/v1/payments/pay_1234567890_abcdefgh/cancel" \
  -H "X-API-Key: qp_test_sk_your_key"
Only payments in pending status can be cancelled.

Idempotency

Use unique merchant_reference values for idempotency. If you submit a payment with a reference that was already used, the API will return the existing payment instead of creating a duplicate.

Best Practices

Validate Amounts

Always validate amounts before submission. Amounts are in minor units (centavos).

Handle Webhooks

Don’t rely on polling. Set up webhooks for real-time status updates.

Store References

Save transaction_id and merchant_reference for reconciliation.

Check Balance

For withdrawals, verify sufficient balance before initiating.