Payments

On this page

PayU Latam-compatible API (Colombia)

A mock of PayU Latam's JSON API, version 4.0, with sandbox semantics for a Colombian account:

  • card payments by number or token, authorizations, captures, voids and refunds;
  • PSE bank transfers, with the bank list and a simulated bank page;
  • card tokenisation from the server and from the browser (PayU.js);
  • the Queries API;
  • signed confirmations to your confirmation URL.

Only test card numbers are accepted, and no money moves.

Testlie is not affiliated with, endorsed or sponsored by PayU. "PayU" is used only to describe API compatibility.

Connect

Create a PayU Latam-compatible gateway under Payment gateways. Use its base URL in place of https://sandbox.api.payulatam.com (or https://api.payulatam.com):

PayU Mock
POST /payments-api/4.0/service.cgi POST <base URL>/payments-api/4.0/service.cgi
POST /reports-api/4.0/service.cgi POST <base URL>/reports-api/4.0/service.cgi
https://gateway.payulatam.com/ppp-web-gateway/javascript/PayU.js <base URL>/ppp-web-gateway/javascript/PayU.js
Token service (payU.setURL) …/payments-api/4.0/service <base URL>/payments-api/4.0/service

Send JSON with Content-Type: application/json and Accept: application/json. The XML flavour of the API is not supported. Every command carries the credentials in the body, as PayU's does:

json
{ "language": "es", "command": "PING", "merchant": { "apiKey": "…", "apiLogin": "…" }, "test": true }
Credential Environment variable Use
API key PAYU_LATAM_API_KEY merchant.apiKey; signs orders, confirmations and response pages
API login PAYU_LATAM_API_LOGIN merchant.apiLogin
Merchant id PAYU_LATAM_MERCHANT_ID In order signatures and confirmations
Account id PAYU_LATAM_ACCOUNT_ID order.accountId; the tokenisation script's account
Public key PAYU_LATAM_PUBLIC_KEY The tokenisation script (payU.setPublicKey)

Answers are PayU's envelopes with HTTP 200: { "code": "SUCCESS", "error": null, … }, or { "code": "ERROR", "error": "<message>" } for wrong credentials, a bad signature or invalid input. Simulated outages (the API failure scenario, rate limits) answer HTTP 429 or 500.

Payments API

Command
PING
GET_BANKS_LIST bankListInformation: { paymentMethod: "PSE", paymentCountry: "CO" }. Returns banks[] with pseCode and description. The first entry, pseCode 0, is PayU's "select a bank" placeholder.
GET_PAYMENT_METHODS The account's methods: VISA, VISA_DEBIT, MASTERCARD, MASTERCARD_DEBIT, AMEX, DINERS, CODENSA, PSE
SUBMIT_TRANSACTION transaction.type: see below
CREATE_TOKEN creditCardToken: { payerId, name, identificationNumber, paymentMethod, number, expirationDate: "YYYY/MM" }. The same card for the same payer returns the same token.
GET_TOKENS creditCardTokenInformation: { payerId } or { creditCardTokenId }, optionally startDate and endDate. Returns creditCardTokenList[] with maskedNumber, never the number.
REMOVE_TOKEN removeCreditCardToken: { payerId, creditCardTokenId }

Transactions

transaction.type Body
AUTHORIZATION_AND_CAPTURE, AUTHORIZATION order (accountId, referenceCode, description, language, signature, notifyUrl, additionalValues.TX_VALUE { value, currency }, buyer), payer, paymentMethod, paymentCountry, and either creditCard { number, securityCode, expirationDate, name }, or creditCardTokenId with creditCard { securityCode } or creditCard { processWithoutCvv2: true }
CAPTURE, VOID order.id and parentTransactionId of an approved AUTHORIZATION. A capture may carry a lower additionalValues.TX_VALUE.
REFUND, PARTIAL_REFUND order.id, parentTransactionId of the approved charge, reason; PARTIAL_REFUND takes additionalValues.TX_VALUE. As in Colombia, an order takes one partial refund, and PSE payments are refunded in full only.

The order signature is hash(ApiKey~merchantId~referenceCode~tx_value~currency) in MD5, SHA1 or SHA256, and the mock accepts any of the three. For example, md5("4Vj8eK4rloUd272L48hsrarnUA~508029~TestPayU~3~USD") is ba9ffa71559580175585e45ce70b6c37. tx_value may be written as sent (3, 3.0 or 3.00). Confirmations and response pages are signed with the algorithm the order used.

Answers carry PayU's transactionResponse:

  • orderId and transactionId;
  • state: APPROVED, DECLINED, PENDING or EXPIRED;
  • responseCode, pendingReason, trazabilityCode, authorizationCode and operationDate;
  • extraParameters.

Refunds answer PENDING with pendingReason PENDING_REVIEW, as PayU reviews them first. They turn APPROVED after the mock's async delay; read the result with the Queries API.

Card outcomes

As in PayU's sandbox, the card holder's name decides, and for a token it's the name it was created with:

Name Result
APPROVED, or any other name APPROVED
REJECTED DECLINED, responseCode ENTITY_DECLINED
PENDING PENDING (PENDING_REVIEW); turns APPROVED after the async delay, and the confirmation is sent then

The Outcome scenario overrides the name:

  • "succeed" approves every payment;
  • "decline" declines with the chosen code: ENTITY_DECLINED, PAYMENT_NETWORK_REJECTED, INSUFFICIENT_FUNDS, INVALID_CARD, CONTACT_THE_ENTITY, EXPIRED_CARD, RESTRICTED_CARD, ANTIFRAUD_REJECTED or INTERNAL_PAYMENT_PROVIDER_ERROR; it also declines refunds;
  • "requires action" leaves payments in review.

Test cards only

A card number must pass the Luhn check and be one of these test numbers. Anything else, including a real card, is refused. Card numbers and security codes are never stored: tokens and transactions keep the masked number, such as 409744******0004, and the call log masks card numbers. Any future expiry date works.

Method Numbers
VISA 4097440000000004, 4037997623271984, 4111111111111111, 4242424242424242
VISA_DEBIT 4509420000000008
MASTERCARD 5471300000000003, 5120697176068275, 5555555555554444
AMEX 377813000000001
DINERS 36032400000007, 36032404150519
CODENSA 5907120000000009

PSE bank transfers

  1. Read the banks with GET_BANKS_LIST.
  2. Submit an AUTHORIZATION_AND_CAPTURE with paymentMethod: "PSE" in COP and these extraParameters:
    • FINANCIAL_INSTITUTION_CODE: a pseCode; 1022 is PayU's test bank;
    • USER_TYPE: N for a person, J for a company;
    • PSE_REFERENCE2: the document type;
    • PSE_REFERENCE3: the document number;
    • RESPONSE_URL;
    • optionally PSE_REFERENCE1, the payer's IP address.
  3. The answer is PENDING, with pendingReason AWAITING_NOTIFICATION, responseCode PENDING_TRANSACTION_CONFIRMATION and extraParameters.BANK_URL.
  4. Send the payer to BANK_URL. It opens the mock's simulated PSE bank page, where you choose Approve the transfer, Reject or Let it expire.
  5. The browser returns to RESPONSE_URL with PayU's response page fields:
    • merchantId, referenceCode, reference_pol, transactionId;
    • transactionState (4 approved, 6 declined, 5 expired, 7 pending) and lapTransactionState;
    • TX_VALUE, currency, cus, pseBank;
    • signature = hash(ApiKey~merchantId~referenceCode~new_value~currency~transactionState), where new_value is TX_VALUE rounded half to even to one decimal.
  6. The confirmation is sent.

With the Outcome scenario set to "decline", PSE transfers are declined at once, with no BANK_URL. The page itself is the "auto" behaviour.

Queries API

POST <base URL>/reports-api/4.0/service.cgi, with details:

Command details result.payload
ORDER_DETAIL { orderId } The order, or null
ORDER_DETAIL_BY_REFERENCE_CODE { referenceCode } Every order with that reference, or null
TRANSACTION_RESPONSE_DETAIL { transactionId } { state, trazabilityCode, authorizationCode, responseCode, operationDate, … }, or null

An order has the following fields:

  • id, referenceCode, description;
  • status: IN_PROGRESS, AUTHORIZED, CAPTURED, CANCELLED, DECLINED or REFUNDED;
  • additionalValues.TX_VALUE;
  • transactions[], each with:
    • id, type, paymentMethod, parentTransactionId;
    • creditCard.maskedNumber;
    • transactionResponse.state;
    • additionalValues.TX_VALUE.

Tokenisation in the browser (PayU.js)

Load the mock's script instead of PayU's and point it at the mock's token service:

html
<script src="<base URL>/ppp-web-gateway/javascript/PayU.js"></script>
<form id="card">
  <input payu-content="number"> <input payu-content="name_card">
  <input payu-content="exp_month"> <input payu-content="exp_year"> <input payu-content="document">
  <input type="hidden" payu-content="payer_id" value="customer-123">
</form>
<script>
  payU.setURL("<base URL>/payments-api/4.0/service");
  payU.setPublicKey("<public key>"); payU.setAccountID("<account id>");
  payU.setListBoxID("methods"); payU.setLanguage("es"); payU.getPaymentMethods();
  payU.createToken((r) => (r.error ? alert(r.error) : console.log(r.token)), document.getElementById("card"));
</script>

The script supports PayU's documented calls:

  • setURL, setPublicKey, setAccountID, setListBoxID, setLanguage;
  • setCardDetails, getPaymentMethods, validateCard;
  • createToken(callback, form).

It sends the fields marked payu-content straight to the mock's token service (JSONP, GET …/service.token, so a page that allows no connect-src works), never to your page or server. Your server then reads the token with GET_TOKENS by payerId.

Allow the mock's origin in your page's script-src. The test cards and holder names above apply.

Confirmations

When a transaction is final, the mock POSTs PayU's confirmation, form-encoded, to the order's notifyUrl, or to the gateway's webhook URL when the order has none. A final transaction is one that is approved, declined, expired, or an approved review.

The fields:

  • merchant_id, reference_sale, reference_pol (the order id), transaction_id;
  • state_pol: 4 approved, 6 declined, 5 expired;
  • response_code_pol: 1 approved, 5 entity declined, 6 insufficient funds, 20 expired, 23 anti-fraud…;
  • response_message_pol, value (two decimals), currency, tax;
  • payment_method_type: 2 card, 4 PSE;
  • payment_method_name, email_buyer, cus, pse_bank, transaction_date, test;
  • sign.

sign = hash(ApiKey~merchant_id~reference_sale~new_value~currency~state_pol), where new_value is value with one decimal when its second decimal is 0 (150000.00 → 150000.0) and with two otherwise (150.25). Compare it to authenticate the confirmation, and answer HTTP 200 to stop retries.

The webhook scenarios (delay, drop, duplicate, bad signature) apply, and failed deliveries are retried.

Not implemented:

  • cash and other alternative payment methods;
  • WebCheckout;
  • 3-D Secure;
  • installment plans;
  • subscriptions (the Recurring Payments API);
  • the XML API;
  • countries other than Colombia.