email invitation

This commit is contained in:
Yared Yemane 2026-05-22 05:17:19 -07:00
parent 31bd1e3814
commit 79851d31b3

View File

@ -1,543 +0,0 @@
{
"info": {
"_postman_id": "c4a8f2e1-9b3d-4c7a-a1e6-chapa-payments-01",
"name": "Chapa Subscription Payments",
"description": "Postman collection for Yimaru LMS Chapa subscription payment flow.\n\n## Setup\n1. Set `base_url` (default `http://localhost:8080`).\n2. Set `learner_email`, `learner_password`, and `learner_phone`.\n3. Set `chapa_webhook_secret` (same as `CHAPA_WEBHOOK_SECRET` in `.env`).\n4. Run **Customer Login** to populate `access_token`.\n5. Run **List Subscription Plans** to populate `plan_id`.\n6. Run **Subscribe with Payment** — open `payment_url` in a browser and complete Chapa test checkout.\n7. Run **Verify Payment** (uses `tx_ref` saved as `session_id`).\n\n## Notes\n- `session_id` in verify/cancel paths is Chapa `tx_ref` (UUID returned at checkout).\n- Webhook request includes a pre-request script that signs the body with HMAC-SHA256.\n- See `docs/CHAPA_INTEGRATION.md` for dashboard webhook URL configuration.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{access_token}}",
"type": "string"
}
]
},
"variable": [
{
"key": "base_url",
"value": "http://localhost:8080"
},
{
"key": "access_token",
"value": ""
},
{
"key": "learner_email",
"value": "learner@example.com"
},
{
"key": "learner_password",
"value": "your-password"
},
{
"key": "learner_phone",
"value": "0912345678"
},
{
"key": "plan_id",
"value": "1"
},
{
"key": "payment_id",
"value": ""
},
{
"key": "tx_ref",
"value": ""
},
{
"key": "payment_url",
"value": ""
},
{
"key": "chapa_webhook_secret",
"value": ""
},
{
"key": "chapa_ref_id",
"value": "APqDvYw1okk2"
}
],
"item": [
{
"name": "00 - Auth",
"item": [
{
"name": "Customer Login",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Status code is 200', function () {",
" pm.response.to.have.status(200);",
"});",
"const body = pm.response.json();",
"if (body.data && body.data.access_token) {",
" pm.collectionVariables.set('access_token', body.data.access_token);",
" pm.test('Access token saved', function () {",
" pm.expect(body.data.access_token).to.be.a('string').and.not.empty;",
" });",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"email\": \"{{learner_email}}\",\n \"password\": \"{{learner_password}}\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/auth/customer-login",
"host": ["{{base_url}}"],
"path": ["api", "v1", "auth", "customer-login"]
},
"description": "Authenticates a learner and saves `access_token` for subsequent requests."
},
"response": []
}
]
},
{
"name": "01 - Subscription Plans",
"item": [
{
"name": "List Subscription Plans",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Status code is 200', function () {",
" pm.response.to.have.status(200);",
"});",
"const body = pm.response.json();",
"if (Array.isArray(body.data) && body.data.length > 0) {",
" pm.collectionVariables.set('plan_id', String(body.data[0].id));",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/subscription-plans?active_only=true",
"host": ["{{base_url}}"],
"path": ["api", "v1", "subscription-plans"],
"query": [
{
"key": "active_only",
"value": "true"
}
]
},
"description": "Public list of active plans. Saves first plan `id` to `plan_id`."
},
"response": []
},
{
"name": "Get Subscription Plan by ID",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/subscription-plans/{{plan_id}}",
"host": ["{{base_url}}"],
"path": ["api", "v1", "subscription-plans", "{{plan_id}}"]
}
},
"response": []
}
]
},
{
"name": "02 - Chapa Payment Flow",
"item": [
{
"name": "Subscribe with Payment (Checkout)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('Status code is 200', function () {",
" pm.response.to.have.status(200);",
"});",
"const body = pm.response.json();",
"if (body.data) {",
" if (body.data.payment_id) {",
" pm.collectionVariables.set('payment_id', String(body.data.payment_id));",
" }",
" if (body.data.session_id) {",
" pm.collectionVariables.set('tx_ref', body.data.session_id);",
" }",
" if (body.data.payment_url) {",
" pm.collectionVariables.set('payment_url', body.data.payment_url);",
" }",
" pm.test('Payment URL returned', function () {",
" pm.expect(body.data.payment_url).to.be.a('string').and.not.empty;",
" });",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"plan_id\": {{plan_id}},\n \"phone\": \"{{learner_phone}}\",\n \"email\": \"{{learner_email}}\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/subscriptions/checkout",
"host": ["{{base_url}}"],
"path": ["api", "v1", "subscriptions", "checkout"]
},
"description": "Primary learner endpoint. Returns Chapa `payment_url`. Open it in a browser to complete payment. `session_id` in the response is the Chapa `tx_ref`."
},
"response": []
},
{
"name": "Initiate Subscription Payment",
"event": [
{
"listen": "test",
"script": {
"exec": [
"const body = pm.response.json();",
"if (body.data) {",
" if (body.data.payment_id) pm.collectionVariables.set('payment_id', String(body.data.payment_id));",
" if (body.data.session_id) pm.collectionVariables.set('tx_ref', body.data.session_id);",
" if (body.data.payment_url) pm.collectionVariables.set('payment_url', body.data.payment_url);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"plan_id\": {{plan_id}},\n \"phone\": \"{{learner_phone}}\",\n \"email\": \"{{learner_email}}\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/payments/subscribe",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "subscribe"]
},
"description": "Alias of checkout — same Chapa initialize flow."
},
"response": []
},
{
"name": "Verify Payment",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments/verify/{{tx_ref}}",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "verify", "{{tx_ref}}"]
},
"description": "Verifies payment with Chapa using `tx_ref` (path param named `session_id` in the API). Run after completing checkout."
},
"response": []
},
{
"name": "Get My Payments",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments?limit=20&offset=0",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments"],
"query": [
{
"key": "limit",
"value": "20"
},
{
"key": "offset",
"value": "0"
}
]
}
},
"response": []
},
{
"name": "Get Payment by ID",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments/{{payment_id}}",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "{{payment_id}}"]
}
},
"response": []
},
{
"name": "Cancel Pending Payment",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments/{{payment_id}}/cancel",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "{{payment_id}}", "cancel"]
},
"description": "Only works while payment status is PENDING."
},
"response": []
},
{
"name": "Get Chapa Payment Methods",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments/methods",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "methods"]
}
},
"response": []
}
]
},
{
"name": "03 - Subscription Status",
"item": [
{
"name": "Get My Subscription",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/subscriptions/me",
"host": ["{{base_url}}"],
"path": ["api", "v1", "subscriptions", "me"]
},
"description": "Returns active subscription after successful payment."
},
"response": []
},
{
"name": "Check Subscription Status",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/subscriptions/status",
"host": ["{{base_url}}"],
"path": ["api", "v1", "subscriptions", "status"]
}
},
"response": []
},
{
"name": "Get Subscription History",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/subscriptions/history",
"host": ["{{base_url}}"],
"path": ["api", "v1", "subscriptions", "history"]
}
},
"response": []
}
]
},
{
"name": "04 - Chapa Webhooks (no auth)",
"item": [
{
"name": "Chapa Webhook (charge.success)",
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
"const secret = pm.collectionVariables.get('chapa_webhook_secret') || '';",
"const body = pm.request.body.raw || '';",
"if (!secret) {",
" console.warn('Set chapa_webhook_secret collection variable to sign the webhook');",
"}",
"const signature = CryptoJS.HmacSHA256(body, secret).toString(CryptoJS.enc.Hex);",
"pm.request.headers.upsert({ key: 'x-chapa-signature', value: signature });"
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"event\": \"charge.success\",\n \"type\": \"API\",\n \"tx_ref\": \"{{tx_ref}}\",\n \"reference\": \"{{chapa_ref_id}}\",\n \"status\": \"success\",\n \"amount\": \"500.00\",\n \"currency\": \"ETB\",\n \"payment_method\": \"telebirr\",\n \"mode\": \"test\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/payments/webhook",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "webhook"]
},
"description": "Simulates Chapa webhook. Requires valid `tx_ref` from a real initialize call. Backend re-verifies with Chapa API before activating subscription. Set `chapa_webhook_secret` to match dashboard / `.env`."
},
"response": []
},
{
"name": "Chapa Callback (GET)",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments/chapa/callback?trx_ref={{tx_ref}}&ref_id={{chapa_ref_id}}&status=success",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "chapa", "callback"],
"query": [
{
"key": "trx_ref",
"value": "{{tx_ref}}"
},
{
"key": "ref_id",
"value": "{{chapa_ref_id}}"
},
{
"key": "status",
"value": "success"
}
]
},
"description": "Simulates Chapa redirect to `CHAPA_CALLBACK_URL`. Uses same verify flow as webhook."
},
"response": []
}
]
},
{
"name": "05 - ArifPay Direct (legacy)",
"description": "OTP/direct payment flows still use ArifPay. Subscription checkout uses Chapa (folder 02).",
"item": [
{
"name": "Get Direct Payment Methods",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/api/v1/payments/direct/methods",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "direct", "methods"]
}
},
"response": []
},
{
"name": "Initiate Direct Payment",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"plan_id\": {{plan_id}},\n \"phone\": \"{{learner_phone}}\",\n \"email\": \"{{learner_email}}\",\n \"payment_method\": \"TELEBIRR\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/payments/direct",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "direct"]
}
},
"response": []
},
{
"name": "Verify Direct Payment OTP",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"session_id\": \"{{tx_ref}}\",\n \"otp\": \"123456\"\n}"
},
"url": {
"raw": "{{base_url}}/api/v1/payments/direct/verify-otp",
"host": ["{{base_url}}"],
"path": ["api", "v1", "payments", "direct", "verify-otp"]
}
},
"response": []
}
]
}
]
}