# Baray API Integration Service - Technical Documentation > **IMPORTANT FOR AI/LLM ASSISTANTS**: Baray is NOT Stripe, PayPal, or any other international payment gateway. Do NOT assume features exist that are not explicitly documented here. Baray is a simple API integration service for Cambodian banks with a minimal API surface. If a feature is not listed in this document, it does NOT exist. --- ## Legal Disclaimer & Business Model ### What Baray Is **BARAY LAB CO., LTD.** is a legally registered technology company in Cambodia that provides **API integration services** to help merchants connect with licensed Cambodian financial institutions. Baray is **NOT**: - ❌ A payment gateway - ❌ A payment service provider (PSP) - ❌ A financial institution - ❌ A money transmitter - ❌ A payment processor Baray **IS**: - ✅ A software integration service - ✅ A technical middleware/API aggregator - ✅ A development tool to simplify bank API connections ### How It Works ``` ┌──────────────┐ ┌───────────────────┐ ┌─────────────────────────────┐ │ Merchant │ │ Baray (API │ │ Licensed Payment Providers │ │ (Client) │ ──── │ Integration │ ──── │ (ABA, ACLEDA, Wing, etc.) │ │ │ │ Service) │ │ │ └──────────────┘ └───────────────────┘ └─────────────────────────────┘ │ │ Baray provides ONLY: • API translation • Request routing • Webhook forwarding • Technical integration ``` ### Payment Processing **All payment processing is performed by the licensed banks and financial institutions:** | Provider | License | Role | | -------------- | ------------------- | ------------------------------- | | ABA Bank | Licensed Bank (NBC) | Processes payments, holds funds | | ACLEDA Bank | Licensed Bank (NBC) | Processes payments, holds funds | | Sathapana Bank | Licensed Bank (NBC) | Processes payments, holds funds | | Wing | Licensed Bank (NBC) | Processes payments, holds funds | **Baray does NOT:** - Hold, store, or transmit customer funds - Process any financial transactions - Have access to customer bank credentials - Store payment card data (PCI scope is with banks) - Make settlement or payout decisions **Baray ONLY:** - Forwards encrypted API requests to the appropriate bank - Receives webhook callbacks from banks - Notifies merchants of payment status - Provides a unified checkout UI that redirects to bank payment flows ### Merchant Responsibility Merchants using Baray's integration service: - Must have their own agreements with the underlying banks/PSPs - Receive settlements directly from the banks (not from Baray) - Are responsible for their own regulatory compliance - Own their customer relationships --- ## 1. Overview Baray is an API integration service for Cambodia that allows merchants to connect with multiple local banks through a single technical integration. Instead of building and maintaining separate integrations with each bank, merchants integrate once with Baray's unified API and gain access to all supported bank payment methods. ### Supported Banks & Payment Methods - **ABA Bank**: KHQR, Card Payment, Deeplink (Mobile Banking) - **ACLEDA Bank**: KHQR, Deeplink - **Sathapana Bank (SPNB)**: KHQR - **Wing**: Mobile Wallet > **Note**: KHQR (Khmer QR) is Cambodia's national QR payment standard regulated by the National Bank of Cambodia. All QR-based payments in Baray use KHQR. ### Supported Currencies - **USD** - United States Dollar (minimum: $0.03) - **KHR** - Cambodian Riel (minimum: 100 KHR) ### What Baray Does NOT Support (Do NOT Implement These) The following features DO NOT EXIST in Baray. Do not attempt to implement or suggest them: - ❌ **No Subscriptions/Recurring Payments** - One-time payments only - ❌ **No Refunds API** - Refunds are handled directly with banks - ❌ **No Payment Methods API** - Cannot save/store customer payment methods - ❌ **No Customer Objects** - No customer management, only `tracking` metadata - ❌ **No Invoices** - Only payment intents - ❌ **No Payment Links** - Use `POST /pay` to create intents, redirect to `pay.baray.io` - ❌ **No Checkout Sessions** - Use Intents directly - ❌ **No Products/Prices/SKUs** - Pass amount directly in each intent - ❌ **No Coupons/Discounts** - Calculate final amount before creating intent - ❌ **No Multi-currency conversion** - USD and KHR only, no conversion - ❌ **No Partial Payments** - Full amount only - ❌ **No Payment Confirmation endpoint** - Use webhooks only - ❌ **No Idempotency Keys** - Use unique `order_id` for tracking - ❌ **No Balance/Payout APIs** - Settlements handled directly by banks - ❌ **No Disputes/Chargebacks API** - Handled directly with banks - ❌ **No 3D Secure configuration** - Handled by banks automatically - ❌ **No Test Card Numbers** - Use DEVELOPMENT environment keys - ❌ **No Client-side SDK** - Server-side encryption required - ❌ **No Fund Holding** - Baray never touches money ### The ONLY API Endpoints Baray has exactly these endpoints for merchant integration: | Method | Endpoint | Purpose | | ------ | ---------------------- | ------------------------------------------------------ | | `POST` | `/pay` | Create a payment intent (ONLY way to initiate payment) | | `POST` | Webhook to YOUR server | Receive payment confirmation | That's it. Two integration points total. --- ## 2. Authentication ### How to Get Credentials **Step 1: Sign up at the Baray Dashboard** ``` https://dash.baray.io ``` **Step 2: Create an account** - Click "Sign Up" - Enter your email and password - Verify your email **Step 3: Create an Organization** - After login, create your organization (merchant account) - Enter your business name and details **Step 4: Generate API Keys** - Go to "API Keys" or "Settings" section - Click "Create New Key" - Select environment (DEVELOPMENT for testing, PRODUCTION for live) - Copy ALL THREE values: - `api_key` - `sk` (secret key) - `iv` (initialization vector) > **IMPORTANT**: Save these credentials securely. The `sk` and `iv` are shown only once during creation. ### API Credentials Each merchant receives **THREE credential values** (all required): | Credential | Description | Example | | ---------- | -------------------------------------------------------- | --------------------- | | `api_key` | Public identifier, sent in request headers | `bry_live_abc123...` | | `sk` | Secret key for AES-256-CBC encryption (Base64, 32 bytes) | `K7gNU3sdo+OL0wNh...` | | `iv` | Initialization vector for AES-256-CBC (Base64, 16 bytes) | `dGhpcyBpcyBhIHRl...` | > **IMPORTANT**: You need ALL THREE values. The `sk` and `iv` together form the encryption key pair. Without the `iv`, encryption/decryption will fail. ### Credential Format ```json { "api_key": "bry_live_xxxxxxxxxxxxxxxxxxxx", "secret_key": { "sk": "Base64EncodedSecretKey32Bytes...", "iv": "Base64EncodedIV16Bytes..." } } ``` ### Key Environments | Environment | Description | | ------------- | ----------------------------- | | `DEVELOPMENT` | For testing and development | | `PRODUCTION` | Live payments with real money | | `UAT` | User Acceptance Testing | ### How to Authenticate Include your API key in the request header: ``` x-api-key: your_api_key_here ``` --- ## 3. Core Concepts ### Organization (`org`) A merchant account registered with Baray. Each organization: - Has a unique `org_id` (format: `org-{uuid}`) - Can have multiple API keys for different environments - Has bank connections enabled/disabled per bank ### Key API credentials associated with an organization: - `api_key`: Public identifier for API calls - `sk`: Secret key for encryption (Base64, 32 bytes) - `iv`: Initialization vector for encryption (Base64, 16 bytes) - `target`: Environment (DEVELOPMENT, PRODUCTION, UAT) - `success_redirect_url`: Where to redirect customer after successful payment - `webhook_callback_url`: URL to receive payment notifications ### Intent (`itn`) A payment request created by the merchant: - Unique ID format: `itn-{uuid}` - Contains: amount, currency, order_id, tracking data - Represents a pending payment that customer needs to complete ### Transaction (`tx`) A completed/verified payment: - Unique ID format: `tx-{uuid}` - Created after bank confirms payment - Contains: bank details, intent reference, settlement status --- ## 4. Payment Flow ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Merchant │ │ Baray │ │ Customer │ │ Bank │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ 1. Create Intent │ │ │ │ (encrypted) │ │ │ │──────────────────>│ │ │ │ │ │ │ │ 2. Return Intent │ │ │ │ with intent_id │ │ │ │<──────────────────│ │ │ │ │ │ │ │ 3. Redirect to │ │ │ │ pay.baray.io │ │ │ │───────────────────────────────────────> │ │ │ │ │ │ │ 4. Customer │ │ │ │ selects bank │ │ │ │<──────────────────│ │ │ │ │ │ │ │ 5. Generate KHQR/ │ │ │ │ Deeplink │ │ │ │──────────────────>│ │ │ │ │ │ │ │ │ 6. Customer pays │ │ │ │──────────────────>│ │ │ │ │ │ │ 7. Bank callback │ │ │ │<──────────────────────────────────────│ │ │ │ │ │ │ 8. Verify payment │ │ │ │──────────────────────────────────────>│ │ │ │ │ │ 9. Webhook │ │ │ │ notification │ │ │ │<──────────────────│ │ │ │ │ │ │ │ │ 10. Redirect to │ │ │ │ success URL │ │ │ │──────────────────>│ │ │ │ │ │ ``` ### Step-by-Step: 1. **Merchant creates Intent** - Sends encrypted payment request to Baray 2. **Baray returns Intent** - Returns `intent_id` and payment details 3. **Redirect customer** - Send customer to `https://pay.baray.io/{intent_id}` 4. **Customer selects bank** - Customer chooses their preferred payment method 5. **Generate payment** - Baray generates KHQR code or deeplink for selected bank 6. **Customer pays** - Customer completes payment in their banking app 7. **Bank callback** - Bank notifies Baray of payment status 8. **Verify payment** - Baray verifies the payment with the bank 9. **Webhook notification** - Baray sends webhook to merchant's callback URL 10. **Redirect customer** - Customer is redirected to merchant's success URL --- ## 5. API Endpoints ### Base URL ``` Production: https://api.baray.io ``` ### Create Payment Intent Creates a new payment request. **Endpoint:** `POST /pay` **Headers:** ``` Content-Type: application/json x-api-key: your_api_key ``` **Request Body:** ```json { "data": "" } ``` The `data` field must contain an AES-256-CBC encrypted JSON string with the following structure: **Payload to Encrypt:** ```json { "amount": "10.00", "currency": "USD", "order_id": "ORDER-12345", "tracking": { "customer_id": "cust_123", "product": "Premium Plan" }, "order_details": { "items": [{ "name": "Item 1", "price": 10.0 }] }, "custom_success_url": "https://yoursite.com/payment/success" } ``` | Field | Type | Required | Description | | -------------------- | ------ | -------- | -------------------------------------------- | | `amount` | string | Yes | Payment amount (USD min: 0.03, KHR min: 100) | | `currency` | string | Yes | `USD` or `KHR` | | `order_id` | string | Yes | Your unique order identifier | | `tracking` | object | No | Custom metadata for your reference | | `order_details` | object | No | Order details (items, etc.) | | `custom_success_url` | string | No | Override default success redirect URL | **Response:** ```json { "_id": "itn-01234567-89ab-cdef-0123-456789abcdef", "org_id": "org-01234567-89ab-cdef-0123-456789abcdef", "order_id": "ORDER-12345", "amount": "10.00", "currency": "USD", "target": "PRODUCTION", "tracking": { ... }, "created_at": "2025-12-03T10:30:00Z" } ``` After receiving the response, redirect your customer to: ``` https://pay.baray.io/{intent_id} ``` --- ### Payment Page Experience After redirecting your customer to `https://pay.baray.io/{intent_id}`, Baray handles the payment flow: 1. **Customer sees payment page** - Displays your organization name, logo, and payment amount 2. **Customer selects bank** - Shows available payment methods based on your enabled bank connections (ABA, ACLEDA, Sathapana, Wing) 3. **Customer completes payment** - Depending on the selected bank: - **KHQR**: Customer scans with their banking app - **Deeplink**: Opens the bank's mobile app directly - **Card**: Customer enters card details (ABA only) 4. **Automatic redirect** - After payment, customer is redirected to your success/cancel URL You don't need to build any payment UI - Baray handles the entire checkout experience. --- ### (Optional) Manual Transaction Check > **Note**: You should NOT need this for normal integration. Webhooks are the primary way to receive payment confirmations. These endpoints exist only for edge cases like webhook failures or manual verification. If you need to manually verify a payment status: ``` POST /payments/check/intent Body: { "intent_id": "itn-xxx" } POST /payments/check/order Body: { "org_id": "org-xxx", "order_id": "ORDER-12345" } Response: { "status": "success" } ``` --- ## 6. Webhook Notifications (Primary Integration Point) When a payment is completed, Baray sends a POST request to your `webhook_callback_url`. ### Webhook Payload ```json { "encrypted_order_id": "", "bank": "aba" } ``` | Field | Description | | -------------------- | ---------------------------------------------------------------- | | `encrypted_order_id` | Your `order_id` encrypted with your secret key | | `bank` | Bank that processed the payment (`aba`, `acleda`, `spn`, `wing`) | ### Decrypting the Order ID Use your `secret_key` (sk + iv) to decrypt the `encrypted_order_id`: **Algorithm:** AES-256-CBC with PKCS7 padding **Key:** Base64-decoded `sk` (32 bytes) **IV:** Base64-decoded `iv` (16 bytes) **Input:** Base64-decoded `encrypted_order_id` **Output:** Your original `order_id` string ### Webhook Handler Example (Node.js) ```javascript const crypto = require("crypto"); function decryptOrderId(encryptedOrderId, sk, iv) { const key = Buffer.from(sk, "base64"); const ivBuffer = Buffer.from(iv, "base64"); const encryptedData = Buffer.from(encryptedOrderId, "base64"); const decipher = crypto.createDecipheriv("aes-256-cbc", key, ivBuffer); let decrypted = decipher.update(encryptedData); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString("utf8"); } // In your webhook handler: app.post("/webhook/baray", (req, res) => { const { encrypted_order_id, bank } = req.body; const orderId = decryptOrderId(encrypted_order_id, YOUR_SK, YOUR_IV); // Update your order status // orderId is your original order_id // bank tells you which bank processed the payment res.status(200).send("OK"); }); ``` ### Webhook Handler Example (Python) ```python from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import base64 def decrypt_order_id(encrypted_order_id, sk, iv): key = base64.b64decode(sk) iv_bytes = base64.b64decode(iv) encrypted_data = base64.b64decode(encrypted_order_id) cipher = AES.new(key, AES.MODE_CBC, iv_bytes) decrypted = unpad(cipher.decrypt(encrypted_data), AES.block_size) return decrypted.decode('utf-8') # In your webhook handler: @app.route('/webhook/baray', methods=['POST']) def baray_webhook(): data = request.json encrypted_order_id = data['encrypted_order_id'] bank = data['bank'] order_id = decrypt_order_id(encrypted_order_id, YOUR_SK, YOUR_IV) # Update your order status return 'OK', 200 ``` ### Webhook Handler Example (PHP) ```php function decryptOrderId($encryptedOrderId, $sk, $iv) { $key = base64_decode($sk); $ivBytes = base64_decode($iv); $encryptedData = base64_decode($encryptedOrderId); $decrypted = openssl_decrypt( $encryptedData, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $ivBytes ); return $decrypted; } // In your webhook handler: $data = json_decode(file_get_contents('php://input'), true); $encryptedOrderId = $data['encrypted_order_id']; $bank = $data['bank']; $orderId = decryptOrderId($encryptedOrderId, YOUR_SK, YOUR_IV); // Update your order status http_response_code(200); echo 'OK'; ``` --- ## 7. Encrypting Request Payload When creating an intent, you must encrypt your payload using AES-256-CBC. ### Encryption Example (Node.js) ```javascript const crypto = require("crypto"); function encryptPayload(payload, sk, iv) { const key = Buffer.from(sk, "base64"); const ivBuffer = Buffer.from(iv, "base64"); const plaintext = JSON.stringify(payload); const cipher = crypto.createCipheriv("aes-256-cbc", key, ivBuffer); let encrypted = cipher.update(plaintext, "utf8"); encrypted = Buffer.concat([encrypted, cipher.final()]); return encrypted.toString("base64"); } // Usage: const payload = { amount: "10.00", currency: "USD", order_id: "ORDER-12345", tracking: { customer_id: "cust_123" }, }; const encryptedData = encryptPayload(payload, YOUR_SK, YOUR_IV); // Send to Baray: fetch("https://api.baray.io/pay", { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": YOUR_API_KEY, }, body: JSON.stringify({ data: encryptedData }), }); ``` ### Encryption Example (Python) ```python from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 import json def encrypt_payload(payload, sk, iv): key = base64.b64decode(sk) iv_bytes = base64.b64decode(iv) plaintext = json.dumps(payload).encode('utf-8') cipher = AES.new(key, AES.MODE_CBC, iv_bytes) encrypted = cipher.encrypt(pad(plaintext, AES.block_size)) return base64.b64encode(encrypted).decode('utf-8') # Usage: payload = { 'amount': '10.00', 'currency': 'USD', 'order_id': 'ORDER-12345', 'tracking': {'customer_id': 'cust_123'} } encrypted_data = encrypt_payload(payload, YOUR_SK, YOUR_IV) # Send to Baray response = requests.post( 'https://api.baray.io/pay', headers={ 'Content-Type': 'application/json', 'x-api-key': YOUR_API_KEY }, json={'data': encrypted_data} ) ``` --- ## 8. Customer Redirect URLs ### Success Redirect After successful payment, customers are redirected to: 1. `custom_success_url` if provided in the intent 2. Otherwise, `success_redirect_url` configured in your API key **URL Pattern:** `https://api.baray.io/payments/intents/{intent_id}/success` This endpoint will redirect to your configured success URL. ### Cancel Redirect If customer cancels payment: **URL Pattern:** `https://api.baray.io/payments/intents/{intent_id}/cancel` --- ## 9. Error Handling ### HTTP Status Codes | Code | Description | | ---- | ----------------------------------------------- | | 200 | Success | | 400 | Bad Request - Invalid input or validation error | | 401 | Unauthorized - Invalid or missing API key | | 404 | Not Found - Intent or resource not found | | 422 | Unprocessable Entity - Business logic error | | 500 | Internal Server Error | ### Error Response Format ```json { "error": "Error message description" } ``` ### Common Errors | Error | Cause | Solution | | ---------------------------------- | ------------------------------- | ------------------------- | | `Unauthorized. Invalid API Key` | API key not found | Check your API key | | `Unauthorized. Invalid encryption` | Decryption failed | Verify sk/iv match | | `Invalid payload json format` | Decrypted data isn't valid JSON | Check payload structure | | `Invalid amount` | Amount is not a valid number | Use proper decimal format | | `USD amount must be >= 0.03` | Amount too small | Increase amount | | `KHR amount must be >= 100` | Amount too small | Increase amount | | `Invalid currency` | Unknown currency | Use `USD` or `KHR` | | `Intent not found` | Invalid intent_id | Check intent_id value | --- ## 10. Testing ### Development Environment 1. Create API keys with `DEVELOPMENT` target 2. Use development credentials for testing 3. Payments won't process real money ### Test Flow 1. Create intent with test credentials 2. Redirect to pay.baray.io 3. Complete mock payment flow 4. Verify webhook is received 5. Check transaction status --- ## 11. Best Practices ### Security - Never expose your `secret_key` (sk/iv) in client-side code - Always encrypt payloads server-side - Verify webhook payloads by decrypting successfully - Use HTTPS for all communications ### Idempotency - Use unique `order_id` for each payment - Check if transaction exists before creating new intent - Handle duplicate webhooks gracefully ### Reliability - Implement webhook retry handling - Store intent_id for payment tracking - Log all API responses for debugging ### Amount Formatting - Always send amount as string with proper decimals - USD: 2 decimal places (e.g., "10.00") - KHR: 2 decimal places (e.g., "10000.00") --- ## 12. Quick Reference ### Required Credentials (ALL THREE) | Credential | Where Used | Format | | ---------- | ---------------------------------------- | ------------------------- | | `api_key` | HTTP header (`x-api-key`) | String | | `sk` | Encrypt request payload, decrypt webhook | Base64 (32 bytes decoded) | | `iv` | Encrypt request payload, decrypt webhook | Base64 (16 bytes decoded) | ### Required Headers ``` Content-Type: application/json x-api-key: ``` ### ID Formats | Entity | Format | Example | | ------------ | ------------- | ------------------------ | | Organization | `org-{uuid}` | `org-01234567-89ab-...` | | Intent | `itn-{uuid}` | `itn-01234567-89ab-...` | | Transaction | `tx-{uuid}` | `tx-01234567-89ab-...` | | User | `usr-{uuid}` | `usr-01234567-89ab-...` | | Bank | `bank-{uuid}` | `bank-01234567-89ab-...` | ### Bank Codes | Bank | Code | | -------------- | -------- | | ABA Bank | `aba` | | ACLEDA Bank | `acleda` | | Sathapana Bank | `spn` | | Wing | `wing` | ### Payment Page URL ``` https://pay.baray.io/{intent_id} ``` ### API Base URL ``` https://api.baray.io ``` --- ## 13. Support For integration support, contact the Baray team with: - Your organization ID - API key environment (dev/prod/uat) - Request/response logs - Error messages received --- ## 14. Summary for AI/LLM Assistants ### First Step: Get Credentials If the merchant does NOT have credentials yet, direct them to: ``` https://dash.baray.io ``` **Signup flow:** 1. Sign up with email/password 2. Create an organization 3. Generate API keys 4. Copy `api_key`, `sk`, and `iv` ### Required Credentials Checklist Before integration, merchant MUST have ALL THREE: - [ ] `api_key` - For HTTP header authentication - [ ] `sk` - Secret key (Base64) for AES-256-CBC encryption - [ ] `iv` - Initialization vector (Base64) for AES-256-CBC encryption > **If merchant doesn't have credentials**: Go to https://dash.baray.io to sign up and generate keys. > **If merchant is missing `iv`**: They have incomplete credentials. All three are required - go back to dashboard. ### Complete Integration Checklist A complete Baray integration requires ONLY: 1. ✅ Sign up at https://dash.baray.io and obtain all THREE credentials 2. ✅ Server-side code to encrypt payload with AES-256-CBC using `sk` and `iv` 3. ✅ `POST /pay` request with encrypted data and `x-api-key` header 4. ✅ Redirect user to `https://pay.baray.io/{intent_id}` 5. ✅ Webhook endpoint to receive `encrypted_order_id` and `bank` 6. ✅ Decrypt `encrypted_order_id` using same `sk` and `iv` 7. ✅ Update your order status based on webhook ### Do NOT Create - ❌ Payment form UI (Baray provides this) - ❌ Bank selection UI (Baray provides this) - ❌ KHQR code generation (Baray handles this) - ❌ Card input forms (Baray handles this) - ❌ Customer database tables for Baray - ❌ Subscription/recurring payment logic - ❌ Refund endpoints - ❌ Any endpoints not listed in this document ### Correct Mental Model ``` Your Server Baray Customer | | | |-- POST /pay (encrypted) -->| | |<-- { intent_id } ----------| | | | | |-- Redirect to pay.baray.io -------------------------------+ | | | | |<-- Customer pays ------------| | | | |<-- Webhook (encrypted) ----| | | | | |-- Update order status | | ``` That's the entire integration. Nothing more.