diff --git a/postman/Chapa-Subscription-Payments.postman_collection.json b/postman/Chapa-Subscription-Payments.postman_collection.json deleted file mode 100644 index 0600468..0000000 --- a/postman/Chapa-Subscription-Payments.postman_collection.json +++ /dev/null @@ -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": [] - } - ] - } - ] -}