WP-PAY-01 · SMS OTP · Expo refresh
2026-07-24
Payments (MoMo / VNPay / MOCK)
| Endpoint | Auth | Mô tả |
|---|---|---|
GET /payments/providers |
public | Trạng thái MOCK/MOMO/VNPAY |
POST /payments/init |
JWT | Tạo PaymentIntent + payUrl |
POST /payments/momo/init |
JWT | Force MoMo |
GET /payments/momo/return |
public | Redirect return → credit ví |
POST /payments/momo/ipn |
public | IPN |
POST /payments/vnpay/init |
JWT | Force VNPay |
GET /payments/vnpay/return |
public | Verify hash → credit ví |
GET /payments/mock/pay?orderId= |
public | Local giả lập thanh toán |
POST /payments/mock/confirm |
public | Smoke confirm |
GET /payments/status/:orderId |
JWT | Trạng thái |
GET /payments/history |
JWT | Lịch sử |
POST /payments/refund |
JWT | Chưa IMP (WP-PAY-02) |
Local: PAYMENTS_MOCK=true (mặc định non-prod).
Prod: điền MOMO_* / VNPAY_*, PAYMENTS_MOCK=false, tắt ALLOW_INTERNAL_WALLET_CREDIT.
Credit ví idempotent: pay:{paymentId} trên wallet_ledger.
SMS OTP
SMS_PROVIDER |
Hành vi |
|---|---|
console (default local) |
Log OTP · trả devOtp non-prod |
esms |
eSMS VN SendMultipleMessage_V4_post_json · ESMS_API_KEY + ESMS_SECRET_KEY |
http |
POST SMS_HTTP_URL JSON { phone, message, code } + SMS_HTTP_API_KEY |
twilio |
Twilio Messages API |
Production: bắt buộc esms | http | twilio (không OTP_DEV_CODE).
Live wire (stop-gate): cần credential thật từ eSMS / Twilio — không commit secret. Trên VPS:
# /opt/welife/api/.env
SMS_PROVIDER=esms
ESMS_API_KEY=...
ESMS_SECRET_KEY=...
ESMS_BRAND_NAME=WELIFE # brand đã duyệt trên eSMS
# bỏ SMS_HTTP_URL placeholder nếu chuyển từ http → esms
docker compose --profile full up -d --force-recreate api
Expo refresh-on-401
mobile / driver / partner src/services/api.ts:
- Request 401
- Single-flight
POST /auth/refresh - Lưu token mới (SecureStore)
- Retry 1 lần
- Fail → clear tokens
Skip refresh cho /auth/otp/*, /auth/refresh, /auth/logout.
Smoke local
# OTP
curl -X POST .../auth/otp/send -d '{"phone":"+84901234567","surface":"CONSUMER"}'
# → smsProvider=console, otpBackend=redis
# Pay MOCK
# 1) OTP verify → Bearer
# 2) POST /payments/init {"amount":10000,"provider":"MOCK"}
# 3) POST /payments/mock/confirm {"orderId":"..."}
# → wallet credit