Checkout & Payments
Complete guide for payment processing with Stripe, PayPal, MercadoPago, and on-site payments.
Overview
LosCenotes supports multiple payment gateways:
| Gateway | Description | Region |
|---|---|---|
| Stripe | Credit/Debit cards | Global |
| PayPal | PayPal accounts | Global |
| MercadoPago | Popular in Latin America | LATAM |
| On-Site | Pay at location | Local |
Payment Flow
1. Calculate Price → /pricing/calculate-complete
2. Validate Reservation → /public/reservations/validate (optional)
3. Create Payment Intent → /checkout/{provider}/create
4. Process Payment → Redirect or Confirm
5. Receive Confirmation → Webhook or Callback
Payment Methods
GET /payment-methods
List all available payment methods.
curl -X GET "https://service-gateway.loscenotes.com/payment-methods" \
-H "Content-Type: application/json"
Response:
{
"success": true,
"message": "payment_methods.list_retrieved_successfully",
"data": [
{
"id": "pm-stripe",
"provider": "stripe",
"name": {
"es": "Tarjeta de crédito/débito",
"en": "Credit/Debit Card"
},
"description": {
"es": "Paga con Visa, Mastercard, Amex",
"en": "Pay with Visa, Mastercard, Amex"
},
"isActive": true,
"supportedCards": ["visa", "mastercard", "amex"],
"iconUrl": "/icons/stripe.svg",
"processingFee": 0,
"minAmount": 1000,
"maxAmount": 10000000,
"requiresNativeProcessing": true
},
{
"id": "pm-paypal",
"provider": "paypal",
"name": {
"es": "PayPal",
"en": "PayPal"
},
"description": {
"es": "Paga con tu cuenta PayPal",
"en": "Pay with your PayPal account"
},
"isActive": true,
"iconUrl": "/icons/paypal.svg",
"processingFee": 0,
"minAmount": 1000,
"maxAmount": 10000000,
"requiresNativeProcessing": false
},
{
"id": "pm-mercadopago",
"provider": "mercadopago",
"name": {
"es": "MercadoPago",
"en": "MercadoPago"
},
"description": {
"es": "Paga con MercadoPago, OXXO, SPEI",
"en": "Pay with MercadoPago, OXXO, SPEI"
},
"isActive": true,
"iconUrl": "/icons/mercadopago.svg",
"processingFee": 0,
"minAmount": 1000,
"maxAmount": 10000000,
"requiresNativeProcessing": false
},
{
"id": "pm-onsite",
"provider": "on_site",
"name": {
"es": "Pago en sitio",
"en": "Pay on-site"
},
"description": {
"es": "Paga al llegar al cenote (efectivo o tarjeta)",
"en": "Pay when you arrive at the cenote (cash or card)"
},
"isActive": true,
"iconUrl": "/icons/cash.svg",
"processingFee": 0,
"minAmount": 1000,
"maxAmount": 5000000,
"requiresNativeProcessing": false
}
],
"pagination": {
"total": 4,
"perPage": 10,
"currentPage": 1,
"lastPage": 1
}
}
GET /payment-methods/active
Get only active payment methods (recommended for frontend).
curl -X GET "https://service-gateway.loscenotes.com/payment-methods/active" \
-H "Content-Type: application/json"
Stripe Payments
POST /checkout/stripe/create-payment-intent
Create a Stripe Payment Intent for card payments.
curl -X POST "https://service-gateway.loscenotes.com/checkout/stripe/create-payment-intent" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"reservationId": "reservation-uuid",
"amount": 157500,
"currency": "MXN",
"customerEmail": "cliente@email.com",
"metadata": {
"cenoteId": "cenote-uuid",
"visitDate": "2025-03-15"
}
}'
Response:
{
"success": true,
"message": "checkout.payment_intent_created_successfully",
"data": {
"clientSecret": "pi_3MQ...secret_abc123",
"paymentIntentId": "pi_3MQExample",
"amount": 157500,
"currency": "mxn",
"status": "requires_payment_method"
}
}
POST /checkout/stripe/confirm
Confirm a Stripe payment after the client has entered card details.
curl -X POST "https://service-gateway.loscenotes.com/checkout/stripe/confirm" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"paymentIntentId": "pi_3MQExample",
"paymentMethodId": "pm_card_visa"
}'
Response:
{
"success": true,
"message": "checkout.payment_confirmed_successfully",
"data": {
"paymentIntentId": "pi_3MQExample",
"status": "succeeded",
"reservationId": "reservation-uuid",
"confirmationCode": "RES-ABC123",
"paidAt": "2025-03-10T15:30:00.000Z"
}
}
PayPal Payments
POST /checkout/paypal/create-order
Create a PayPal order for payment.
curl -X POST "https://service-gateway.loscenotes.com/checkout/paypal/create-order" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"reservationId": "reservation-uuid",
"amount": 157500,
"currency": "MXN",
"returnUrl": "https://yourapp.com/payment/success",
"cancelUrl": "https://yourapp.com/payment/cancel"
}'
Response:
{
"success": true,
"message": "checkout.paypal_order_created_successfully",
"data": {
"orderId": "5O190127TN364715T",
"approvalUrl": "https://www.paypal.com/checkoutnow?token=5O190127TN364715T",
"status": "CREATED"
}
}
POST /checkout/paypal/capture
Capture a PayPal payment after the user has approved it.
curl -X POST "https://service-gateway.loscenotes.com/checkout/paypal/capture" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"orderId": "5O190127TN364715T"
}'
Response:
{
"success": true,
"message": "checkout.paypal_payment_captured_successfully",
"data": {
"orderId": "5O190127TN364715T",
"status": "COMPLETED",
"reservationId": "reservation-uuid",
"confirmationCode": "RES-ABC123",
"paidAt": "2025-03-10T15:30:00.000Z"
}
}
MercadoPago Payments
POST /checkout/mercadopago-native/create-preference
Create a MercadoPago preference for Checkout Pro.
curl -X POST "https://service-gateway.loscenotes.com/checkout/mercadopago-native/create-preference" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"reservationId": "reservation-uuid",
"items": [
{
"title": "Cenote Dos Ojos - 2 Adultos",
"quantity": 1,
"unitPrice": 1575,
"currency": "MXN"
}
],
"payer": {
"email": "cliente@email.com",
"name": "Juan Pérez"
},
"backUrls": {
"success": "https://yourapp.com/payment/success",
"failure": "https://yourapp.com/payment/failure",
"pending": "https://yourapp.com/payment/pending"
}
}'
Response:
{
"success": true,
"message": "checkout.mercadopago_preference_created_successfully",
"data": {
"preferenceId": "1234567890-abc123-def456",
"checkoutUrl": "https://www.mercadopago.com.mx/checkout/v1/redirect?pref_id=1234567890-abc123-def456",
"initPoint": "https://www.mercadopago.com.mx/checkout/v1/redirect?pref_id=1234567890-abc123-def456"
}
}
Note: Redirect the user to
checkoutUrlto complete payment on MercadoPago's hosted page.
On-Site Payments
POST /checkout/on-site
Register an on-site payment (pay at location).
curl -X POST "https://service-gateway.loscenotes.com/checkout/on-site" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"reservationId": "reservation-uuid",
"paymentMethod": "cash",
"notes": "Customer will pay in cash at arrival"
}'
Response:
{
"success": true,
"message": "checkout.on_site_payment_registered",
"data": {
"reservationId": "reservation-uuid",
"status": "pending_payment",
"confirmationCode": "RES-ABC123",
"paymentDue": "on_arrival"
}
}
Reservation Validation
POST /public/reservations/validate
Pre-validate a reservation before creating it.
curl -X POST "https://service-gateway.loscenotes.com/public/reservations/validate" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_api_key" \
-d '{
"cenoteId": "cenote-uuid",
"date": "2025-03-15",
"startTime": "09:00",
"endTime": "12:00",
"ageBreakdown": {
"adult": 2,
"child": 1
},
"guestEmail": "cliente@email.com",
"guestName": "Juan Pérez"
}'
Response:
{
"success": true,
"message": "reservation.validation_completed",
"data": {
"isValid": true,
"availability": {
"available": true,
"remainingCapacity": 45
},
"pricing": {
"basePrice": 35000,
"totalVisitors": 3,
"subtotal": 91000,
"discounts": 0,
"total": 91000,
"currency": "MXN"
},
"warnings": [],
"errors": []
}
}
Webhooks
Payment confirmations are also sent via webhooks. Configure your webhook URL in the partner portal.
Webhook Events
| Event | Description |
|---|---|
payment.completed | Payment was successful |
payment.failed | Payment failed |
payment.refunded | Payment was refunded |
reservation.confirmed | Reservation confirmed |
reservation.cancelled | Reservation cancelled |
Webhook Payload
{
"event": "payment.completed",
"timestamp": "2025-03-10T15:30:00.000Z",
"data": {
"reservationId": "reservation-uuid",
"confirmationCode": "RES-ABC123",
"paymentId": "payment-uuid",
"amount": 157500,
"currency": "MXN",
"paymentMethod": "stripe",
"paidAt": "2025-03-10T15:30:00.000Z"
}
}
Error Handling
Payment Errors
| Error | Description |
|---|---|
card_declined | Card was declined |
insufficient_funds | Insufficient funds |
expired_card | Card has expired |
invalid_cvc | Invalid security code |
processing_error | Payment processing error |
Best Practice
try {
const result = await createPaymentIntent(data);
// Handle success
} catch (error) {
if (error.code === 'card_declined') {
showError('Tu tarjeta fue rechazada. Intenta con otra.');
} else if (error.code === 'insufficient_funds') {
showError('Fondos insuficientes en tu tarjeta.');
} else {
showError('Error al procesar el pago. Intenta de nuevo.');
}
}
Currency
All prices in the API are returned in CENTS (centavos).
| Amount | Cents | Display |
|---|---|---|
| $350.00 MXN | 35000 | (35000 / 100).toFixed(2) |
| $1,575.00 MXN | 157500 | (157500 / 100).toFixed(2) |
| $15.00 USD | 1500 | (1500 / 100).toFixed(2) |
// Convert cents to display
const formatPrice = (cents, currency = 'MXN') => {
const amount = cents / 100;
return new Intl.NumberFormat('es-MX', {
style: 'currency',
currency: currency
}).format(amount);
};
console.log(formatPrice(157500)); // "$1,575.00"