email invitation
This commit is contained in:
parent
31bd1e3814
commit
79851d31b3
|
|
@ -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": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user