Pago directo
Integración de pago directo
Para realizar una integración de pago directo (con tarjeta de crédito y/o débito), es decir, sin ser redireccionado al checkout de OrkestaPay, es necesario seguir los siguientes pasos.
- Autenticación de servicios
- Tokenizar tarjeta
- Registrar orden
- Registrar pago
1.- Autenticación de servicios
Deberás de copiar las credenciales de acceso al API, para llamar al servicio de autenticación de OrkestaPay y obtener un token de acceso que se utilizará para llamar al resto de servicios.
Petición hacia el servicio
Después de copiar las credenciales, deberás de buscar los textos REPLACE_WITH_YOUR_CLIENT_ID
y REPLACE_WITH_YOUR_CLIENT_SECRET
en el script de abajo y reemplazarlos con los valores copiados para poder ejecutar la llamada al servicio mediante shell:
client_id
: Llave de accesoclient_secret
: Llave secreta
Tipo de permisos
La propiedad grant_type
siempre se deberá de llevar el valor client_credentials
, dado que es el modelo de funcionamiento del protocolo oAuth 2.0 que utiliza OrkestaPay para autenticar los servicios.
curl --request POST \
--url https://api.sand.orkestapay.com/v1/oauth/tokens \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '
{
"client_id": "REPLACE_WITH_YOUR_CLIENT_ID",
"client_secret": "REPLACE_WITH_YOUR_CLIENT_SECRET",
"grant_type": "client_credentials"
}
'
Respuesta del servicio
Como resultado de la llamada al servicio de autenticación se regresará un token JWT, el cual se utilizará en todos los siguientes llamados a los servicios del API de OrkestaPay.
{
"token_type": "Bearer",
"expires_in": 1800,
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwenJMTnEwbzBab1R4NTlaeWVPaTI1RGxZLWl6cV91SVFSLThWS0RaWjlFIn0.eyJleHAiOjE2Njk4NTAzNTAsImlhdCI6MTY2OTg1MDE3MCwianRpIjoiMWI4MWZhMDItMzk2ZC00NGNjLWJlMzctZGU4ZWQyODg2MTEyIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYXV0aC56ZW5raS5maS9hdXRoL3JlYWxtcy9wYnciLCJzdWIiOiIxMjgyNjJhOS00NDgxLTQ4OGItYTczNi1iNmI5MTA1NjQ4MzQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiI1MDg3ODE3MDhjNzk5MTE5NTJkZGJlYWZkZjM5NjNmNTcxYjNjYzE4YzE5YmNkY2YiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vcG9ydGFsLWRldi56ZW5raS5maSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiYXBpIiwiYXBpX3plbmtpcGF5Il19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImNsaWVudEhvc3QiOiIzNS44NS4yMy4xOTAiLCJjbGllbnRJZCI6IjUwODc4MTcwOGM3OTkxMTk1MmRkYmVhZmRmMzk2M2Y1NzFiM2NjMThjMTliY2RjZiIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC01MDg3ODE3MDhjNzk5MTE5NTJkZGJlYWZkZjM5NjNmNTcxYjNjYzE4YzE5YmNkY2YiLCJjbGllbnRBZGRyZXNzIjoiMzUuODUuMjMuMTkwIn0.Ds5eQ-tkn4ckTUHI-mrJn6eYBaUa-6uZNxzrGRfYc5neI1TvB2RHu_IDsktDVi9XdR5P_P0mSpzar9jWJOrxxA_csTnn9ZXy8rDeRqjMm9j03xWz-tZcxiUM6xvN1qvOeBGFzISIP9y24jyL0Jqpl8YhkSGF8xBfFvfhOvEMvgLby5n7dTDoZVi2Bw8G1kZJKPejmBu8MJetl08OoVk_obp6lW3YetQPYTwsutOc_yIxBIUkPSH2Gj3wpBxBa8EfMES4J1SAT7Thpw_CmZ_PNB9rEDUJI4bzE7QM2Z0n4LNXzbo5JFuWudKwfhqOcryH0slmHOamJgbtR5EGryf8LQ"
}
2.- Tokenizar tarjeta
Por medidas de seguridad es necesario tokenizar la tarjeta desde el cliente web, para evitar que esta información viaje al servidor del comercio, para ello es necesario utilizar la librería de Javascript de OrkestPay (https://checkout.orkestapay.com/script/orkestapay.js).
También es necesario generar un device_session_id
, el cual servirá como mecanismo de prevención de fraudes, es decir, ayudará a identificar los dispositivos desde el cual se realizan los pagos.
Ambos datos deben ser enviados al servidor para continuar con el proceso de pago.
Para llevar a cabo la implementación, son necesarias las credenciales obtenidas desde el dashboard de OrkestaPay:
- ID del comercio
- Llave pública
A continuación un ejemplo básico de la implementación:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example 1</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/>
</head>
<body>
<main class="container">
<form id="payment-form">
<section class="row mt-5">
<h1 id="card-title" class="col-12 text-center"></h1>
<article class="pt-3 col-12 col-lg-4">
<label for="card-number" class="form-label">Card number</label>
<input
type="text"
id="card-number"
class="form-control"
autocomplete="cc-number"
inputmode="numeric"
name="cc-number"
/>
<small id="card-number-error" class="text-danger"></small>
</article>
<article class="pt-3 col-6 col-lg-2">
<label for="card-expiration" class="form-label">Expiration</label>
<input
type="text"
id="card-expiration"
class="form-control"
placeholder="MM/YY"
autocomplete="cc-exp"
name="cc-exp"
/>
<small id="card-expiration-error" class="text-danger"></small>
</article>
<article class="pt-3 col-6 col-lg-2">
<label for="card-csc" class="form-label">CSC</label>
<input
type="text"
id="card-csc"
class="form-control"
autocomplete="cc-csc"
inputmode="numeric"
name="cc-csc"
/>
<small id="card-csc-error" class="text-danger"></small>
</article>
<article class="pt-3 col-12 col-lg-4">
<label for="card-promotions" class="form-label">Promotions</label>
<select
name="card-promotions"
id="card-promotions"
class="form-select"
></select>
</article>
<article class="pt-3 col-12 col-lg-4">
<label for="card-holder-name" class="form-label">Holder name</label>
<input
type="text"
id="card-holder-name"
class="form-control"
autocomplete="cc-given-name"
name="cc-given-name"
/>
<small id="card-holder-name-error" class="text-danger"></small>
</article>
<article class="pt-3 col-12 col-lg-4">
<label for="card-holder-last-name" class="form-label">
Holder last name
</label>
<input
type="text"
id="card-holder-last-name"
class="form-control"
autocomplete="cc-additional-name"
name="cc-additional-name"
/>
<small id="card-holder-last-name-error" class="text-danger"></small>
</article>
<article
class="pt-3 col-12 col-lg-4 d-flex justify-content-end align-items-end"
>
<button
type="button"
id="btn-create-payment-method"
class="btn btn-primary"
>
Create payment method
</button>
</article>
<article class="pt-3 col-12">
<div
id="payment-method-details"
class="w-100 alert"
role="alert"
></div>
</article>
</section>
</form>
</main>
<script
type="text/javascript"
src="https://checkout.orkestapay.com/script/orkestapay.js"
></script>
<script type="text/javascript">
const currency = 'MXN';
const total_amount = '100.00';
const card_number_id = 'card-number';
const expiration_date_id = 'card-expiration';
const verification_code_id = 'card-csc';
const holder_name_id = 'card-holder-name';
const holder_last_name_id = 'card-holder-last-name';
(async function main() {
initTitle();
const orkestapay = createOrkestaPay();
const orkestapay_card = await createOrkestaPayCard(orkestapay);
getDeviceSessionId(orkestapay);
handleButtonClickEvent(orkestapay_card);
handlePromotionChanges(orkestapay_card);
handleErrorEventChanges(orkestapay_card);
})();
function initTitle() {
const card_title_id = 'card-title';
const card_title = document.getElementById(card_title_id);
card_title.textContent = `Create a payment method with ${currency} $${total_amount}`;
}
function createOrkestaPay() {
const is_sandbox = true;
const merchant_id = '{YOUR_MERCHANT_ID}';
const public_key = '{YOUR_PUBLIC_KEY_OR_DEVICE_KEY}';
return initOrkestaPay({
is_sandbox,
merchant_id,
public_key,
});
}
function createOrkestaPayCard(orkestapay) {
const card_number = document.getElementById(card_number_id);
const expiration_date = document.getElementById(expiration_date_id);
const verification_code = document.getElementById(verification_code_id);
const holder_name = document.getElementById(holder_name_id);
const holder_last_name = document.getElementById(holder_last_name_id);
const promotions_params = { currency, total_amount };
return orkestapay.createCard({
card_number,
expiration_date,
verification_code,
holder_name,
holder_last_name,
promotions_params,
});
}
function handleButtonClickEvent(orkestapay_card) {
const btn_create_payment_method_id = 'btn-create-payment-method';
const btn_create_payment_method = document.getElementById(
btn_create_payment_method_id
);
btn_create_payment_method.addEventListener('click', async () => {
await createPaymentMethod(orkestapay_card);
});
}
async function createPaymentMethod(orkestapay_card) {
const payment_method_details_id = 'payment-method-details';
const payment_method_details = document.getElementById(
payment_method_details_id
);
try {
const one_time_use = true;
const payment_method = await orkestapay_card.createToken({
one_time_use,
});
payment_method_details.classList.remove('alert-danger');
payment_method_details.classList.add('alert-success');
payment_method_details.textContent = JSON.stringify(
payment_method,
null,
2
);
} catch (error) {
payment_method_details.classList.remove('alert-success');
payment_method_details.classList.add('alert-danger');
payment_method_details.textContent = parseError2String(error);
logError(
createPaymentMethod.name,
orkestapay_card.createToken.name,
error
);
}
}
function handlePromotionChanges(orkestapay_card) {
const card_promotions_id = 'card-promotions';
const card_promotions = document.getElementById(card_promotions_id);
orkestapay_card.card_number.promotions$.subscribe((promotions) => {
card_promotions.replaceChildren();
const option = document.createElement('option');
option.value = null;
option.textContent = 'Select promotion';
card_promotions.appendChild(option);
for (const type of promotions) {
for (const promotion of type.installments) {
const option = document.createElement('option');
option.value = promotion;
option.textContent = `${promotion} ${type.type}`;
card_promotions.appendChild(option);
}
}
});
}
function getDeviceSessionId(orkestapay) {
orkestapay.getDeviceInfo().then((data) => {
const device_session_input = document.createElement("input");
device_session_input.type = "hidden";
device_session_input.value = data.device_session_id;
device_session_input.name = "device_session_id";
const container = document.getElementById("payment-form");
container.appendChild(device_session_input);
}).catch(err => console.error(err))
}
function handleErrorEventChanges(orkestapay_card) {
const card_number_error_id = `${card_number_id}-error`;
const expiration_date_error_id = `${expiration_date_id}-error`;
const verification_code_error_id = `${verification_code_id}-error`;
const holder_name_error_id = `${holder_name_id}-error`;
const holder_last_name_error_id = `${holder_last_name_id}-error`;
const card_number_error =
document.getElementById(card_number_error_id);
const expiration_date_error = document.getElementById(
expiration_date_error_id
);
const verification_code_error = document.getElementById(
verification_code_error_id
);
const holder_name_error =
document.getElementById(holder_name_error_id);
const holder_last_name_error = document.getElementById(
holder_last_name_error_id
);
orkestapay_card.card_number.errors$.subscribe((error) => {
card_number_error.textContent = parseError2String(error);
logError(handleErrorEventChanges.name, card_number_id, error);
});
orkestapay_card.expiration_date.errors$.subscribe((error) => {
expiration_date_error.textContent = parseError2String(error);
logError(handleErrorEventChanges.name, expiration_date_id, error);
});
orkestapay_card.verification_code.errors$.subscribe((error) => {
verification_code_error.textContent = parseError2String(error);
logError(handleErrorEventChanges.name, verification_code_id, error);
});
orkestapay_card.holder_name.errors$.subscribe((error) => {
holder_name_error.textContent = parseError2String(error);
logError(handleErrorEventChanges.name, holder_name_id, error);
});
orkestapay_card.holder_last_name.errors$.subscribe((error) => {
holder_last_name_error.textContent = parseError2String(error);
logError(handleErrorEventChanges.name, holder_last_name_id, error);
});
}
function parseError2String(error) {
return error?.message ?? '';
}
function logError(origin, name, error) {
error && console.error(origin, name, error.code, error);
}
</script>
</body>
</html>
2.1.- Generar Device Session ID
Como se menciona en el punto anterior, es necesario generar un device_session_id
, el cual servirá como mecanismo de prevención de fraudes, es decir, ayudará a identificar los dispositivos desde el cual se realizan los pagos.
Este valor es requerido al momento de mandar llamar al servicio del registro de pago, es decir, será necesario enviarlo al servidor junto con el token de tarjeta.
Si bien en el ejemplo antes descrito viene el ejemplo de forma integral, aquí lo detallaremos.
- Inicializar la instancia de Orkestapay con las credenciales.
- Llamar a la función
getDeviceInfo()
que resuelve una promesa con eldevice_session_id
. - Registrar el valor devuelto en un input dentro del formulario que enviará los datos al servidor. También este valor puede quedar asignado en algún estado de la aplicación, dependiendo de la tecnología de frontend que estes utilizando.
const orkestapay = initOrkestaPay({ merchant_id, public_key, is_sandbox });
getDeviceSessionId(orkestapay);
function getDeviceSessionId(orkestapay) {
orkestapay.getDeviceInfo().then((data) => {
const device_session_input = document.createElement("input");
device_session_input.type = "hidden";
device_session_input.value = data.device_session_id;
device_session_input.name = "device_session_id";
const container = document.getElementById("payment-form");
container.appendChild(device_session_input);
}).catch(err => console.error(err))
}
Documentación
Para más información sobre la librería de Javascript, puedes consultar nuestra documentación de Headless Checkout.
3.- Registrar orden
En este paso, se detallan los artículos y montos por cuales se está realizando el cobro al cliente, prácticamente el checkout de compra.
Petición hacia el servicio
Se deberá llamar al servicio de "crear orden" y como parte de la petición se deberá de enviar como path parameter el ID de la orden (ORDER_ID
).
curl --request POST \
--url https://api.sand.orkestapay.com/v1/orders \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer REPLACE_WITH_YOUR_ACCESS_TOKEN' \
--data '
{
"merchant_order_id": "1366656595193",
"currency": "MXN",
"subtotal_amount": 1000,
"country_code": "MX",
"discounts": [
{
"amount": 10
}
],
"total_amount": 990,
"products": [
{
"id": "7197",
"name": "Pantalla TCL Smart TV Serie A3 A343 HD Android TV 40",
"quantity": 1,
"unit_price": 1000
}
],
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
}
}
'
Respuesta del servicio
Al registrar la orden, se devolverá la siguiente información. Por el momento, solo será de interés el order_id
, el cual necesitaremos para completar el pago en el siguiente y último paso.
{
"order_id": "ord_a73c91e6f6f949a3a39c9557f353d308",
"status": "CREATED",
"expires_at": "1713566914212",
"merchant_order_id": "1366656595193",
"country": "México",
"country_code": "MX",
"currency": "MXN",
"taxes": [],
"discounts": [
{
"amount": 10
}
],
"subtotal_amount": 1000,
"total_amount": 990,
"products": [
{
"product_id": "7197",
"quantity": 1,
"unit_price": 1000,
"name": "Pantalla TCL Smart TV Serie A3 A343 HD Android TV 40"
}
],
"customer": {
"customer_id": "cus_414bae1120844159bf10f1d6c7b30d74",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"created_at": "1713480514197",
"updated_at": "1713480514197"
},
"placed_at": "1713480514267",
"metadata": {}
}
Documentación API
Para obtener más información sobre el servicio de órdenes, visita nuestra documentación del API de OrkestaPay: https://orkestapay.readme.io/reference/create-order
4.- Registrar pago
El último paso es registrar el pago con la información obtenida en los pasos anteriores.
Petición hacia el servicio
Se deberá llamar al servicio de "crear pago" y como parte de la petición el token de la tarjeta (payment_method_id
) y el device_session_id
que fueron obtenidos en el paso 1 y que son generado desde el cliente web, adicional también se deberá de enviar la cabecera Idempotency-Key
con un valor único por cada nueva petición de pago.
curl --request POST \
--url https://api.sand.orkestapay.com/v1/payments \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: adffaf7a3141474555d988fbb5e43e85' \
--header 'Authorization: Bearer REPLACE_WITH_YOUR_ACCESS_TOKEN' \
--data '
{
"payment_source": {
"type" : "CARD",
"payment_method_id": "{{REPLACE_WITH_CARD_TOKEN}}",
"settings": {
"card": {
"capture":true
}
}
},
"device_session_id": "{{REPLACE_WITH_DEVICE_SESSION_ID}}",
"order_id": "{{REPLACE_WITH_ORDER_ID}}"
}
'
Respuesta del servicio
Al registrar el pago, se regresará la siguiente información. En caso de que el pago haya sido exitoso el status será COMPLETED.
{
"payment_id": "pay_99a7678b55ac437394817d823c888573",
"order_id": "ord_a73c91e6f6f949a3a39c9557f353d308",
"status": "COMPLETED",
"payment_source": {
"type": "CARD",
"settings": {
"card": {
"capture": true
}
},
"payment_method_id": "pym_3863efa7d67d49099cdfeb7a0746b8d5"
},
"amount": {
"captured": 990,
"currency": "MXN"
},
"transactions": [
{
"type": "PURCHASE",
"transaction_id": "ctx_4657f781dbbd4b3ca59a7fcf4db93ebe",
"status": "SUCCESS",
"amount": 990,
"code": "APPROVED",
"message": "Approved or completed successfully",
"description": "Shopping World",
"provider": {
"merchant_provider_id": "mpv_4b324c00f62f41b39d4e8d3f0415e39b",
"name": "Stripe",
"provider_transaction_id": "ctx_4657f781dbbd4b3ca59a7fcf4db93ebe",
"message": "Transaction approved",
"code": ""
},
"authorization_code": "pi_3P6azvGYqhDa3Ul61yfIB9ih",
"created_at": "1713369973147"
}
],
"created_at": "1713369969765",
"updated_at": "1713369969765"
}
Documentación API
Para obtener más información sobre el servicio de órdenes, visita nuestra documentación del API de OrkestaPay: https://orkestapay.readme.io/reference/create-payment
Así concluye la integración de "Pago Directo" con OrkestaPay.
NOTA: Es recomendable que durante el flujo guardes los ID's o datos que consideres relevantes para tu integración.
Updated 4 days ago