Skip to content

Payment Processing

Part of: MPAC SmartPOS Cloud Platform - Product RequirementsVersion: 2.0 Last Updated: 2026-01-28


Overview

The Payment Processing domain handles all payment-related operations in the SmartPOS system, supporting multiple payment methods (QR codes, credit cards, cash, e-money) with different processing modes. It orchestrates payment flows between the terminal, backend services, and the Payment Gateway (mpac-pgw), ensuring secure transaction processing, proper state management, idempotency, and comprehensive refund capabilities. This domain is critical for completing customer transactions and maintaining financial integrity across the platform.

Table of Contents


Payment Methods

Supported Payment Types:

MethodTypeProvider ExamplesProcessing Mode
QR (MPM)DigitalPayPay, LINE Pay, au PAYpgw_processed
QR (CPM)DigitalPayPay, Rakuten Paypgw_processed
Credit CardCardVia SP-NET processorexternal
CashPhysicalDirectmanual
E-MoneyDigitalSuica, PASMO, nanacoexternal

Processing Modes:

  • pgw_processed - Payment Gateway handles directly (QR payments)
  • external - External app processes (Credit Card via Intent)
  • manual - Staff confirms manually (Cash)

Payment Flow Architecture

QR Payment Flow (pgw_processed)

1. Customer selects PayPay
   └─ Device: POST /payments {bill_id, amount, payment_method}

2. svc-smarttab creates payment record
   └─ Call PGW: POST /v1/payment_intents
      Request: {
        merchant_ref_id: "unique_per_merchant",
        amount: 100000,
        currency: "JPY",
        order_id: "POS-ORDER-123",
        payment_method_type: "qr_mpm",
        processing_mode: "pgw_processed"
      }
      Response: PaymentIntent {
        id: "pi_01HXYZ",
        status: "requires_payment_method",
        payment_token: "tok_abc123"
      }

3. Frontend (WebView) receives payment_token
   └─ SDK: POST /v1/payment_intents/{id}/confirm
      └─ PGW generates QR code
      └─ Display QR to customer

4. Customer scans with PayPay app
   └─ PayPay processes payment
   └─ Webhook: POST /v1/webhooks/paypay
   └─ PGW updates: status = "succeeded"

5. Frontend polls: GET /v1/payment_intents/{id}
   └─ Receives: status = "succeeded"

6. svc-smarttab updates payment
   └─ Payment status: completed
   └─ Bill status: paid
   └─ Generate receipt

Credit Card Flow (external)

1. Customer selects Credit Card
   └─ Device: POST /payments {bill_id, amount, payment_method: CREDIT_CARD}

2. svc-smarttab creates payment
   └─ Call PGW: POST /v1/payment_intents
      processing_mode: "external"
      status: "requires_action"

3. Frontend launches Credit Card App via Intent
   Intent Extras: {
     "corr_id": "CORR-36-1730188800000",
     "amount": 100000,
     "classificationCode": 5,
     "authorizationID": "AUTH01"
   }

4. Credit Card App processes
   └─ Communicates with SP-NET processor
   └─ Card reader processes payment
   └─ Returns via ActivityResult: {
        approval_code: "123456",
        card_slip_number: "00123",
        card_last_4: "1234",
        status: "success"
      }

5. Business App receives result
   └─ PATCH /payments/{id}
      {approval_code, card_document_number}
   └─ Status: completed

6. svc-smarttab updates
   └─ Call PGW: POST /v1/payment_intents/{id}/capture
   └─ Bill status: paid

Cash Flow (manual)

1. Customer pays cash
   └─ Device: POST /payments {
        bill_id,
        amount,
        payment_method: CASH,
        cash_received: 150000
      }

2. svc-smarttab creates payment
   └─ Calculate change: 150000 - 100000 = 50000
   └─ Display change to staff
   └─ Staff confirms cash received
   └─ Payment status: completed
   └─ Bill status: paid

Payment Entity

json
{
  "id": "payment_uuid",
  "merchant_id": 1,
  "store_id": 2,
  "bill_id": "bill_uuid",
  "split_id": "split_uuid" (optional),
  "payment_provider_method_id": 1,
  "amount": 100000,
  "tip_amount": 10000,
  "total_amount": 110000,
  "payment_method": {
    "type": "CREDIT_CARD",
    "provider": "VISA"
  },
  "status": "pending|processing|completed|failed|refunded",
  "transaction_id": "pi_01HXYZ",
  "payment_token": "tok_abc123",
  "approval_code": "123456",
  "card_document_number": "00123",
  "card_last_4": "1234",
  "card_brand": "VISA",
  "card_arn": "ARN123...",
  "cash_received": 150000 (if CASH),
  "change_amount": 50000 (if CASH),
  "refund_amount": 0,
  "refund_reason": null,
  "created_at": "2026-01-28T11:00:00Z",
  "completed_at": "2026-01-28T11:01:00Z"
}

Payment States:

  • pending - Created, awaiting processing
  • processing - Being processed by provider
  • completed - Successfully completed
  • failed - Payment failed
  • refunded - Payment refunded

Refund Processing

Purpose: Reverse completed payments with proper audit trail.

Refund Types:

  • Full refund - Return entire payment amount
  • Partial refund - Return portion of payment amount

Refund Flow:

Staff initiates refund
  └─ POST /payments/{id}/refund
     Request: {
       refund_amount: 50000,
       reason: "Item not available",
       staff_code: "STAFF001"
     }

For Credit Card:
  └─ Retrieve: card_document_number, approval_code
  └─ Launch Credit Card App with refund params
  └─ App processes refund via SP-NET
  └─ Receive refund approval
  └─ Update payment: status=refunded, refund_amount

For QR:
  └─ Call PGW: POST /v1/payment_intents/{id}/refund
  └─ PGW calls provider (PayPay) refund API
  └─ Provider refunds to customer account
  └─ Update payment: status=refunded

Update Bill:
  └─ If full refund and only payment: Bill status=cancelled
  └─ If partial: Adjust bill paid_amount

Refund Constraints:

  • Must be completed payment
  • Refund amount ≤ original payment amount
  • Requires staff authorization
  • Cannot refund already refunded payment
  • Audit trail records all refund operations

API Endpoints:

  • POST /payments/{id}/refund - Initiate refund
  • GET /payments/{id}/refund-status - Check refund status

Idempotency & Duplicate Prevention

Purpose: Prevent duplicate charges when requests are retried.

Idempotency Key:

  • Header: Idempotency-Key: <uuid>
  • Unique per transaction per merchant
  • 24-hour cache retention in Redis
  • Database constraint on merchant_ref_id

Behavior:

Request 1: POST /payments {Idempotency-Key: abc-123}
  → Creates payment, caches response
  → Returns: Payment {id: pay_001, status: completed}

Request 2 (retry): POST /payments {Idempotency-Key: abc-123}
  → Detects duplicate key in cache
  → Returns: Same cached response {id: pay_001, status: completed}
  → No duplicate charge created

See Also

Related Domains:

  • Order & Bill - Order lifecycle and bill management that precedes payment
  • Payment Gateway - Payment Gateway service (mpac-pgw) implementation and provider integrations
  • Settlement - Post-payment settlement and reconciliation processes

Technical Implementation:

API Reference:

  • API Endpoints - Complete API specification for payment operations

Navigation: ← Previous: Order & Bill | ↑ Back to Domain Catalog | Next: Payment Gateway →

MPAC — MP-Solution Advanced Cloud Service