feat: add Paystack payment integration#28737
feat: add Paystack payment integration#28737MarvelNwachukwu wants to merge 4 commits intocalcom:mainfrom
Conversation
There was a problem hiding this comment.
3 issues found across 24 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/app-store/paystack/api/webhook.ts">
<violation number="1" location="packages/app-store/paystack/api/webhook.ts:112">
P1: Webhook idempotency check is non-atomic, allowing concurrent duplicate events to both process payment success and trigger duplicate side effects.</violation>
</file>
<file name="packages/app-store/paystack/components/EventTypeAppSettingsInterface.tsx">
<violation number="1" location="packages/app-store/paystack/components/EventTypeAppSettingsInterface.tsx:146">
P2: Clearing the number input stores NaN in refundDaysCount because parseInt("") is NaN; this can propagate into saved app data and the controlled value prop.</violation>
</file>
<file name="packages/app-store/paystack/components/PaystackPaymentComponent.tsx">
<violation number="1" location="packages/app-store/paystack/components/PaystackPaymentComponent.tsx:43">
P2: Paystack amount display incorrectly divides by 100 for all currencies, causing 100× under-display for zero-decimal currencies (e.g., XOF/RWF).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
packages/app-store/paystack/components/EventTypeAppSettingsInterface.tsx
Outdated
Show resolved
Hide resolved
packages/app-store/paystack/components/PaystackPaymentComponent.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/app-store/paystack/api/add.ts">
<violation number="1" location="packages/app-store/paystack/api/add.ts:15">
P2: Invalid `teamId` query values are coerced to `NaN` and treated as falsy, bypassing team-admin validation and silently falling back to user-scoped install instead of returning a validation error.</violation>
<violation number="2" location="packages/app-store/paystack/api/add.ts:22">
P2: Non-atomic `findFirst`→`create` install flow can create duplicate credentials under concurrent requests.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
Add Paystack as a payment provider for Cal.com, enabling hosts to collect payments via Paystack when attendees book events. Paystack is widely used across Africa and supports NGN, GHS, ZAR, KES, and USD currencies. Key components: - PaymentService implementing IAbstractPaymentService (create, refund, update) - PaystackClient REST wrapper for Paystack API (initialize, verify, refund) - Webhook endpoint with HMAC-SHA512 signature verification - Verify endpoint for callback-based payment confirmation - Event type app card UI for configuring price, currency, and refund policy - Setup page for entering Paystack API keys (public + secret) - PaystackPaymentComponent using Paystack inline popup checkout - App install flow via add.ts with isOAuth: true for proper setup routing Follows existing payment app patterns (HitPay, Stripe) for: - App store registration (config.json, _metadata.ts, zod schemas) - Server-side props for setup page authentication - Generated file registration (payment.services, apps.metadata, etc.) - Seed script entry for local development
420431f to
cf5507f
Compare
- Webhook: atomic idempotency via updateMany WHERE success=false
- Webhook: rollback success flag if Paystack API verification fails
- EventTypeSettings: guard parseInt("") returning NaN on cleared input
- PaymentComponent: use convertFromSmallestToPresentableCurrencyUnit for zero-decimal currencies
- Add handler: validate teamId is numeric before access check
- Add handler: consolidate owner filter, return 409 for duplicate installs
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/app-store/paystack/api/webhook.ts">
<violation number="1" location="packages/app-store/paystack/api/webhook.ts:114">
P1: Idempotency lock conflates processing lock with final success state, causing unrecoverable stuck payments when exceptions occur after setting `success: true`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
Wraps verification + handlePaymentSuccess in try/catch that resets success=false on failure, so webhook retries can re-process the payment instead of it being stuck as "already processed".
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/app-store/paystack/api/webhook.ts">
<violation number="1" location="packages/app-store/paystack/api/webhook.ts:145">
P1: New rollback catch resets `payment.success` even after successful processing (because `handlePaymentSuccess` throws 200 by design), breaking idempotency and enabling duplicate side effects.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| appSlug: "paystack", | ||
| traceContext, | ||
| }); | ||
| } catch (processingError) { |
There was a problem hiding this comment.
P1: New rollback catch resets payment.success even after successful processing (because handlePaymentSuccess throws 200 by design), breaking idempotency and enabling duplicate side effects.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app-store/paystack/api/webhook.ts, line 145:
<comment>New rollback catch resets `payment.success` even after successful processing (because `handlePaymentSuccess` throws 200 by design), breaking idempotency and enabling duplicate side effects.</comment>
<file context>
@@ -120,31 +120,36 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
+ appSlug: "paystack",
+ traceContext,
+ });
+ } catch (processingError) {
+ // Rollback so webhook retries can re-process this payment
await prisma.payment.update({
</file context>
|
This would be a great addition 🔥 |
Summary
Adds Paystack as a payment provider for Cal.com. Paystack is Africa's leading payment gateway, processing payments for 200,000+ businesses across Nigeria, Ghana, South Africa, Kenya, and more.
Why: Cal.com currently supports Stripe, PayPal, and a few crypto payment options — none of which serve the African market well. Paystack fills this gap by providing a payment option that supports local currencies (NGN, GHS, ZAR, KES, USD) and local payment methods (bank transfers, mobile money, USSD) that African users expect.
What's included
IAbstractPaymentServicewithcreate,refund,update, anddeletePaymentmethods/payment/[uid]pageisOAuth: truepattern (same as HitPay) to route through the accounts step →add.ts→ setup pageArchitecture
Follows the established Cal.com payment app patterns used by HitPay and Stripe:
Payment flow
PENDINGstatus → redirect to/payment/[uid]/api/integrations/paystack/verify→ payment verified → booking confirmedTest plan
/apps/paystack→ verify setup page appearsScreenshots