Marketplace SPA — Phase 3: Payment Integration (Stripe, Clickpesa, Bank) #21

Closed
opened 2026-03-24 22:47:28 +00:00 by mik-tf · 1 comment
Member

Context

Phase 2 (#19, #20) delivered ed25519 keypair identity + signature auth + session persistence (v0.5.0). Phase 3 adds payment gateways so users can top up their wallet with MC (Mycelium Credits).

Reference implementation: freezone (znzfreezone_code/) has working Stripe + Clickpesa + Bank transfer.

Architecture

User clicks "Buy Credits" in wallet
  → SPA shows amount + gateway selection
  → POST /api/payments/initiate { amount, currency, gateway }
  → Backend creates Payment record + calls provider
  → Provider returns payment_url (Stripe Checkout / Clickpesa link)
  → SPA redirects user to payment_url
  → User completes payment
  → Gateway sends webhook to backend
  → Backend verifies signature, confirms payment
  → CreditService.top_up(user_id, amount) → wallet balance updated
  → User returns to SPA, sees updated balance

Backend Changes

1. PaymentProvider trait (src/providers/payment.rs)

pub trait PaymentProvider: Send + Sync {
    fn name(&self) -> &str;
    fn is_demo(&self) -> bool;
    fn initiate_checkout(&self, amount: f64, currency: &str, reference: &str,
        description: &str, customer_email: &str) -> Result<PaymentCheckout, String>;
    fn parse_webhook(&self, body: &[u8], signature: Option<&str>) -> Result<PaymentWebhookEvent, String>;
}

2. Three providers

  • StripeProvider — Checkout Session API, webhook signature (HMAC-SHA256)
  • ClickPesaProvider — JWT auth, checkout link API, webhook HMAC
  • BankTransferProvider — returns bank details page URL, manual admin confirmation

3. Webhook endpoints

  • POST /api/webhooks/stripe — verify signature, parse event, credit wallet
  • POST /api/webhooks/clickpesa — verify HMAC, parse event, credit wallet

4. Payment API

  • POST /api/payments/initiate — start payment, return URL
  • GET /api/payments/:id — check payment status

5. CreditService

  • top_up(user_id, amount, reference) — add MC to wallet
  • Integrated with existing WalletManager trait

Frontend Changes

Buy Credits flow (wallet page)

  • "Buy Credits" button → modal with amount input + gateway selector (Stripe/Clickpesa/Bank)
  • Initiate payment → redirect to gateway
  • Return URL → show confirmation + updated balance

Bank Transfer page

  • Display bank details (name, account, SWIFT)
  • Show payment reference
  • "Waiting for admin confirmation" status

Environment Variables

DEMO_PAYMENT=true  # skip real providers in dev
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
CLICKPESA_CLIENT_ID=...
CLICKPESA_API_SECRET=...
CLICKPESA_WEBHOOK_SECRET=...
BANK_NAME=...
BANK_ACCOUNT_NAME=...
BANK_ACCOUNT_NUMBER=...
BANK_SWIFT_CODE=...

Testing

  • Demo mode: all providers return fake URLs, webhooks simulated
  • Stripe test mode: use sk_test_ keys
  • Curl test webhook endpoints with HMAC signatures
  • Browser test: Buy Credits → redirect → return → balance updated

Signed-off-by: mik-tf

## Context Phase 2 (#19, #20) delivered ed25519 keypair identity + signature auth + session persistence (v0.5.0). Phase 3 adds payment gateways so users can top up their wallet with MC (Mycelium Credits). Reference implementation: freezone (`znzfreezone_code/`) has working Stripe + Clickpesa + Bank transfer. ## Architecture ``` User clicks "Buy Credits" in wallet → SPA shows amount + gateway selection → POST /api/payments/initiate { amount, currency, gateway } → Backend creates Payment record + calls provider → Provider returns payment_url (Stripe Checkout / Clickpesa link) → SPA redirects user to payment_url → User completes payment → Gateway sends webhook to backend → Backend verifies signature, confirms payment → CreditService.top_up(user_id, amount) → wallet balance updated → User returns to SPA, sees updated balance ``` ## Backend Changes ### 1. PaymentProvider trait (`src/providers/payment.rs`) ```rust pub trait PaymentProvider: Send + Sync { fn name(&self) -> &str; fn is_demo(&self) -> bool; fn initiate_checkout(&self, amount: f64, currency: &str, reference: &str, description: &str, customer_email: &str) -> Result<PaymentCheckout, String>; fn parse_webhook(&self, body: &[u8], signature: Option<&str>) -> Result<PaymentWebhookEvent, String>; } ``` ### 2. Three providers - [x] **StripeProvider** — Checkout Session API, webhook signature (HMAC-SHA256) - [x] **ClickPesaProvider** — JWT auth, checkout link API, webhook HMAC - [x] **BankTransferProvider** — returns bank details page URL, manual admin confirmation ### 3. Webhook endpoints - `POST /api/webhooks/stripe` — verify signature, parse event, credit wallet - `POST /api/webhooks/clickpesa` — verify HMAC, parse event, credit wallet ### 4. Payment API - `POST /api/payments/initiate` — start payment, return URL - `GET /api/payments/:id` — check payment status ### 5. CreditService - `top_up(user_id, amount, reference)` — add MC to wallet - Integrated with existing WalletManager trait ## Frontend Changes ### Buy Credits flow (wallet page) - "Buy Credits" button → modal with amount input + gateway selector (Stripe/Clickpesa/Bank) - Initiate payment → redirect to gateway - Return URL → show confirmation + updated balance ### Bank Transfer page - Display bank details (name, account, SWIFT) - Show payment reference - "Waiting for admin confirmation" status ## Environment Variables ```env DEMO_PAYMENT=true # skip real providers in dev STRIPE_SECRET_KEY=sk_test_... STRIPE_WEBHOOK_SECRET=whsec_... CLICKPESA_CLIENT_ID=... CLICKPESA_API_SECRET=... CLICKPESA_WEBHOOK_SECRET=... BANK_NAME=... BANK_ACCOUNT_NAME=... BANK_ACCOUNT_NUMBER=... BANK_SWIFT_CODE=... ``` ## Testing - Demo mode: all providers return fake URLs, webhooks simulated - Stripe test mode: use `sk_test_` keys - Curl test webhook endpoints with HMAC signatures - Browser test: Buy Credits → redirect → return → balance updated Signed-off-by: mik-tf
Author
Member

Phase 3 Complete — v0.6.0 Released

Commits

Repo Commit What
marketplace_backend e07d3b1 PaymentProvider trait + Stripe/Clickpesa/Bank/Demo + webhooks + payment API
marketplace_frontend 094360a Buy Credits modal calls payment API + redirects to gateway

Releases

Test results

Test Result
cargo check backend PASS
cargo check frontend PASS
Payment initiation (demo) PASS — returns checkout URL
Stripe webhook PASS — accepts + processes events
Payment status API PASS
Smoke tests (26/26) PASS
Deployed to dev PASS

What was built

  1. PaymentProvider traitinitiate_checkout() + parse_webhook() + 4 implementations
  2. StripeProvider — Checkout Sessions API, HMAC-SHA256 webhook verification
  3. ClickPesaProvider — JWT token auth, checkout link generation, HMAC webhook
  4. BankTransferProvider — bank details URL, min $1000, manual admin confirmation
  5. DemoPaymentProvider — instant confirmation, fake URLs (default)
  6. Webhook endpoints — POST /api/webhooks/stripe, POST /api/webhooks/clickpesa
  7. Payment API — POST /api/payments/initiate, GET /api/payments/status
  8. Frontend — Buy Credits modal with Stripe/Clickpesa/Bank selector → API call → redirect

Next: Phase 4 — Signed Wallet Transactions

Signed-off-by: mik-tf

## Phase 3 Complete — v0.6.0 Released ### Commits | Repo | Commit | What | |------|--------|------| | `marketplace_backend` | `e07d3b1` | PaymentProvider trait + Stripe/Clickpesa/Bank/Demo + webhooks + payment API | | `marketplace_frontend` | `094360a` | Buy Credits modal calls payment API + redirects to gateway | ### Releases - [frontend v0.6.0](https://forge.ourworld.tf/mycelium_code/projectmycelium_marketplace_frontend/releases/tag/v0.6.0) - [backend v0.6.0](https://forge.ourworld.tf/mycelium_code/projectmycelium_marketplace_backend/releases/tag/v0.6.0) - [deploy v0.6.0](https://forge.ourworld.tf/mycelium_code/projectmycelium_marketplace_deploy/releases/tag/v0.6.0) ### Test results | Test | Result | |------|--------| | `cargo check` backend | PASS | | `cargo check` frontend | PASS | | Payment initiation (demo) | PASS — returns checkout URL | | Stripe webhook | PASS — accepts + processes events | | Payment status API | PASS | | Smoke tests (26/26) | PASS | | Deployed to dev | PASS | ### What was built 1. **PaymentProvider trait** — `initiate_checkout()` + `parse_webhook()` + 4 implementations 2. **StripeProvider** — Checkout Sessions API, HMAC-SHA256 webhook verification 3. **ClickPesaProvider** — JWT token auth, checkout link generation, HMAC webhook 4. **BankTransferProvider** — bank details URL, min $1000, manual admin confirmation 5. **DemoPaymentProvider** — instant confirmation, fake URLs (default) 6. **Webhook endpoints** — POST /api/webhooks/stripe, POST /api/webhooks/clickpesa 7. **Payment API** — POST /api/payments/initiate, GET /api/payments/status 8. **Frontend** — Buy Credits modal with Stripe/Clickpesa/Bank selector → API call → redirect ### Next: Phase 4 — Signed Wallet Transactions Signed-off-by: mik-tf
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coopcloud_code/home#21
No description provided.