POS

Overview

The Amwal POS component enables merchants to accept digital payments through their Point of Sale systems with a built-in numeric keypad interface and real-time payment processing.

Screenshots

1. Amount Entry Screen


Users enter payment amounts using the built-in numeric keypad_

2. QR Code Display


QR code is automatically generated for customers to scan_

3. Payment Processing


Real-time payment status monitoring with loading indicator

4. Payment Success


Success confirmation with transaction details_

5. Error Handling


Clear error messages with retry options_

Quick Start

Installation

<script type="module" src="https://cdn.jsdelivr.net/npm/amwal-checkout-button@latest/dist/checkout/checkout.esm.js"></script>

Basic Setup

<!-- Add the component to your HTML -->
<amwal-pos
  auth-token="your-auth-token-here"
  store-id="your-store-id-here"
  api-base-url="https://backend.sa.amwal.tech"
  currency="SAR"
  locale="en"
  min-amount="1"
  max-amount="999999"
  customer-first-name="Ahmed"
  customer-last-name="Al-Rashid"
  customer-email="[email protected]"
  customer-phone="+966501234567"
  metadata='{"store_location": "Riyadh Main Branch", "cashier_id": "EMP001", "terminal": "POS-001"}'
  callback-url="https://your-pos-system.com/webhook/payment-complete"
  debug="true"
></amwal-pos>

Or programmatically:

// Create element
const amwalPos = document.createElement('amwal-pos');

// Set properties
amwalPos.authToken = 'your-auth-token-here';
amwalPos.storeId = 'your-store-id-here';
amwalPos.apiBaseUrl = 'https://backend.sa.amwal.tech';
amwalPos.currency = 'SAR';
amwalPos.locale = 'en'; // 'en' or 'ar'
amwalPos.minAmount = 1;
amwalPos.maxAmount = 999999;

// Customer info
amwalPos.customerFirstName = 'Ahmed';
amwalPos.customerLastName = 'Al-Rashid';
amwalPos.customerEmail = '[email protected]';
amwalPos.customerPhone = '+966501234567';

// POS metadata (JSON string)
amwalPos.metadata = JSON.stringify({
  store_location: 'Riyadh Main Branch',
  cashier_id: 'EMP001',
  terminal: 'POS-001'
});

amwalPos.callbackUrl = 'https://your-pos-system.com/webhook/payment-complete';
amwalPos.debug = true;

// Add to DOM
document.getElementById('pos-container').appendChild(amwalPos);

Payment Status Flow

The component transitions through these screens:

ScreenDescription
amount-entryUser enters payment amount using keypad
qr-codeQR code displayed for customer to scan
processingChecking payment status automatically
successPayment completed successfully
errorPayment failed or error occurred

API Status Responses

The component monitors these status values:

Payment Link Status:

  • Paid - Payment completed successfully
  • Failed - Payment failed
  • Cancelled - Payment was cancelled

Transaction Status:

  • success - Transaction completed
  • failed - Transaction failed
  • error - Transaction error occurred

Configuration Options

All configuration is done via HTML attributes or JavaScript properties:

<!-- HTML Attributes (kebab-case) -->
<amwal-pos
  auth-token="string"              <!-- Required: API authorization token -->
  store-id="string"                <!-- Required: Store identifier -->
  api-base-url="string"           <!-- Required: API endpoint -->
  
  currency="SAR"                   <!-- Payment currency (default: SAR) -->
  locale="en"                      <!-- Interface language: 'en' or 'ar' (default: 'en') -->
  min-amount="1"                   <!-- Minimum transaction amount (default: 1) -->
  max-amount="999999"             <!-- Maximum transaction amount (default: 999999) -->
  
  payment-title="string"           <!-- Custom payment title -->
  payment-description="string"    <!-- Custom payment description -->
  
  customer-first-name="string"     <!-- Customer first name -->
  customer-last-name="string"      <!-- Customer last name -->
  customer-email="string"          <!-- Customer email -->
  customer-phone="string"          <!-- Customer phone (+966XXXXXXXXX) -->
  customer-street1="string"        <!-- Address line 1 -->
  customer-street2="string"        <!-- Address line 2 -->
  customer-city="string"           <!-- Customer city -->
  customer-state="string"          <!-- Customer state/province -->
  customer-country="string"        <!-- Customer country (ISO code) -->
  customer-postcode="string"       <!-- Customer postal code -->
  
  address-required="false"         <!-- Require address input (default: false) -->
  sms-language="ar"               <!-- SMS language: 'ar' or 'en' -->
  send-sms="false"                <!-- Send SMS notifications (default: false) -->
  passkey-enabled="false"         <!-- Enable passkey authentication (default: false) -->
  only-show-bank-installments="false"  <!-- Show only bank installments (default: false) -->
  only-show-pay-in-full="false"      <!-- Show only full payment (default: false) -->
  
  callback-url="string"           <!-- Webhook URL for notifications -->
  metadata="string"               <!-- JSON string with custom data -->
  
  debug="false"                   <!-- Enable debug logging (default: false) -->
></amwal-pos>

JavaScript Properties

const amwalPos = document.querySelector('amwal-pos');

// Required properties
amwalPos.authToken = 'string';
amwalPos.storeId = 'string';
amwalPos.apiBaseUrl = 'string';

// Payment configuration
amwalPos.currency = 'SAR'; // Currently only SAR supported
amwalPos.locale = 'en'; // 'en' or 'ar'
amwalPos.minAmount = 1;
amwalPos.maxAmount = 999999;

// Transaction details
amwalPos.paymentTitle = 'Custom Title';
amwalPos.paymentDescription = 'Custom Description';

// Customer information
amwalPos.customerFirstName = 'Ahmed';
amwalPos.customerLastName = 'Al-Rashid';
amwalPos.customerEmail = '[email protected]';
amwalPos.customerPhone = '+966501234567';
amwalPos.customerStreet1 = '123 Main St';
amwalPos.customerCity = 'Riyadh';
amwalPos.customerCountry = 'SA';

// Payment options
amwalPos.addressRequired = false;
amwalPos.smsLanguage = 'ar';
amwalPos.sendSms = true;
amwalPos.passkeyEnabled = false;
amwalPos.onlyShowBankInstallments = false;
amwalPos.onlyShowPayInFull = false;

// Integration
amwalPos.callbackUrl = 'https://yoursite.com/webhook';
amwalPos.metadata = JSON.stringify({
  store_location: 'Main Branch',
  cashier_id: 'EMP001',
  terminal: 'POS-001'
});

// Development
amwalPos.debug = true;

Webhook Integration

Set up your webhook endpoint to receive payment notifications:

// Your webhook endpoint
app.post('/webhook/payment-complete', (req, res) => {
  const payment = req.body;
  
  switch (payment.status) {
    case 'success':
      console.log('Payment completed:', payment.transactionId);
      printReceipt(payment);
      break;
      
    case 'fail':
      console.log('Payment failed:', payment.errorMessage);
      notifyFailure(payment);
      break;
      
    case 'refunded':
      console.log('Payment refunded:', payment.refundAmount);
      printRefundReceipt(payment);
      break;
  }
  
  res.status(200).send('OK');
});

Webhook Payload Example

{
  "transactionId": "txn_1234567890",
  "status": "success",
  "amount": "125.50",
  "currency": "SAR",
  "timestamp": "2025-08-12T10:30:00Z",
  "customer": {
    "firstName": "Ahmed",
    "lastName": "Al-Rashid",
    "phone": "+966501234567"
  },
  "metadata": {
    "store_location": "Riyadh Main Branch",
    "cashier_id": "EMP001",
    "terminal": "POS-001"
  }
}

Component Lifecycle

The component automatically:

  1. Amount Entry: Shows numeric keypad for amount input
  2. QR Generation: Creates payment link and generates QR code
  3. Status Monitoring: Automatically checks payment status every 10 seconds (max 5 attempts)
  4. Result Display: Shows success or error screen based on payment outcome
  5. Reset: Allows starting a new transaction

Receipt Generation

The component provides transaction details that can be used for receipt generation:

// Listen for payment completion via webhook
app.post('/webhook/payment-complete', (req, res) => {
  const paymentData = req.body;
  
  if (paymentData.status === 'success') {
    generateReceipt({
      storeName: 'Your Store Name',
      transactionId: paymentData.transactionId,
      amount: paymentData.amount,
      currency: paymentData.currency,
      timestamp: new Date().toLocaleString('ar-SA'),
      paymentMethod: 'Amwal Digital Payment',
      terminal: JSON.parse(paymentData.metadata || '{}').terminal
    });
  }
  
  res.status(200).send('OK');
});

function generateReceipt(data) {
  const receiptText = `
================================
${data.storeName}
================================

Transaction ID: ${data.transactionId}
Amount: ${data.amount} ${data.currency}
Payment Method: ${data.paymentMethod}
Terminal: ${data.terminal}

Date: ${data.timestamp}

================================
Thank you for your business!
================================
  `;
  
  // Send to your POS printer
  sendToPrinter(receiptText);
}

Programmatic Control

// Get component reference
const amwalPos = document.querySelector('amwal-pos');

// Set amount programmatically (before user input)
amwalPos.amount = '25.50';

// Get current state
console.log('Current screen:', amwalPos.currentScreen);
console.log('Current amount:', amwalPos.amount);
console.log('Transaction ID:', amwalPos.transactionId);

// Reset component for new transaction
amwalPos.amount = '';
amwalPos.currentScreen = 'amount-entry';

Testing

Test Scenarios

  1. Successful Payment: Enter amount → Process → Receive success
  2. Payment Failure: Enter amount → Process → Handle failure
  3. Network Issues: Start payment → Disconnect → Reconnect
  4. Refund Processing: Complete payment → Process refund
  5. Invalid Amount: Enter invalid amount → Show validation error

Test Configuration

<amwal-pos
  auth-token="d9ccf8bc-ed63-44ad-a54c-9d8fee63df6b"
  store-id="f24267bd-79f4-433f-a9f2-485c171301e4"
  api-base-url="https://backend.sa.amwal.tech"
  debug="true"
  min-amount="1"
  max-amount="1000"
  locale="en"
  customer-first-name="Test"
  customer-last-name="User"
  customer-email="[email protected]"
  customer-phone="+966501234567"
  metadata='{"test_mode": true}'
></amwal-pos>

Internal API Calls

The component makes these API calls automatically:

Create Payment Link

POST /payment_links/{storeId}/create
Authorization: {authToken}
Content-Type: application/json

{
  "amount": 125.50,
  "title": "POS Payment - SAR 125.50",
  "singleUse": true,
  "description": "Payment for SAR 125.50",
  "selectedDate": "2025-08-13T10:30:00.000Z",
  "client_first_name": "Ahmed",
  "client_last_name": "Al-Rashid",
  "client_email": "[email protected]",
  "client_phone_number": "+966501234567",
  // ... other customer fields
  "language": "en",
  "sms_language": "ar",
  "send_sms": true,
  "callback_url": "https://yoursite.com/webhook",
  "metadata": {
    "pos_transaction": true,
    "timestamp": "2025-08-12T10:30:00.000Z",
    "store_location": "Main Branch",
    "cashier_id": "EMP001"
  }
}

Check Payment Status

POST /payment_links/{paymentLinkId}/details
Authorization: {authToken}
Content-Type: application/json

{}

Response:

{
  "payment_link": {
    "id": "link_123",
    "status": "Paid"
  },
  "transactions": [
    {
      "id": "txn_456",
      "status": "success"
    }
  ]
}

Support

Troubleshooting

Common Issues:

  1. Component not loading: Check internet connection and script tag is correct
  2. Authentication error: Verify auth-token and store-id are correct
  3. QR code not showing: Check API credentials and network connectivity
  4. Amount validation error: Ensure amount is within min-amount and max-amount range
  5. Payment stuck in processing: Component automatically retries status checks 5 times

Debug Mode:

<amwal-pos debug="true"></amwal-pos>

This shows detailed console logs for troubleshooting.

Console Logs (when debug=true):

[AmwalPos] Debug Mode Enabled
[AmwalPos] Current Screen: amount-entry
[AmwalPos] Creating payment link with payload: {...}
[AmwalPos] Payment link created: {...}
[AmwalPos] Payment check attempt 1/5
[AmwalPos] Payment status response: {...}