Stripe Payments
Candidate Portal Payment Flow
sequenceDiagram StripePaymentForm->>Dreambound API: graphql/getOrCreatePaymentIntent(customerId, payPastDueOnly); Dreambound API->>Stripe API: lookup or create stripe user; Stripe API-->>Dreambound API: return stripe user; Note over Dreambound API,StripePaymentForm: insert pending StripePayment with paymentAmount, chargeIds, stripeCustomerId, etc; Dreambound API-->>StripePaymentForm: getOrCreatePaymentIntent returns clientSecret, paymentAmount; StripePaymentForm->>Stripe API: customer submits payment; Stripe API-->>StripePaymentForm: redirects to return url /dashboard/make-payment; Stripe API-->>Dreambound API: api/stripe/webhook/handleStripePaymentIntentSuccess; Note over Dreambound API,Stripe API: StripePayment looked up & marked as paid, <br /> InvoicePayment inserted <br /> InvoiceCharges marked as paid; Dreambound API-->>StripePaymentForm: Payments table is updated;
Notes
/common/ui/Dashboard/sections/MakePayment- StripePaymentForm.tsx
- receives payPastDueOnly prop to pass to endpoint to determine paymentAmount
- The payment amount has to be generated on the server when the payment intent is created
- The customer will only have this option if they have unpaid charges with a past due date
- receives payPastDueOnly prop to pass to endpoint to determine paymentAmount
- StripePaymentForm.tsx
/graphql/stripePayment- getOrCreatePaymentIntent
- checks for an existing payment intent for the payer, if none,
- creates a pending payment both on stripe and in our system
- returns the clientSecret and the payment amount that has been set on the stripe payment intent
- getOrCreatePaymentIntent
/api/stripe/webhook(express endpoints)- handleStripePaymentIntentSuccess
- see diagram above
- candidate portal flow, external payments are explicitly ignored here
- stripeCheckoutCompleteWebhook
- see diagram below
- external payment flow: the checkout webhook response includes an email that allows us to look up the customer in our database. If the customer isn't found in our database, we cannot log the payment internally so no action is taken.
- handleStripePaymentIntentSuccess
External Payment Flow
Dreambound staff sometimes send the customer a request for payment directly via stripe, bypassing our website. These records can be identified as StripePayment records where the metadata property isExternalPayment = true
sequenceDiagram External Stripe Payment->>Stripe API: customer submits payment; Stripe API->>Dreambound API: api/stripe/webhook/stripeCheckoutCompleteWebhook; Note over Dreambound API,Stripe API: Dreambound customer is looked up by email <br /> IF NOT FOUND, NO FURTHER ACTION TAKEN; Dreambound API-->>Stripe API: lookup or create stripe user; Stripe API-->>Dreambound API: return stripe user; Note over Dreambound API,Stripe API: StripePayment inserted, InvoicePayment inserted; Dreambound API-->>Dreambound Web: Payments table updated;
Environment Variables
- STRIPE_SECRET_KEY
- STRIPE_PUBLISHABLE_KEY
- STRIPE_PRODUCT_ID: A stripe product was created in the stripe dashboard to group all website payments together
- STRIPE_WEBHOOK_SECRET: This key has to be set locally for testing
Cli and local testing
- get access to a dreambound stripe developer account
- full instructions here
- login via cli
stripe login - important: instead of just
stripe listen, usestripe listen --forward-to localhost:3000/api/stripe/webhook - you will be given a webhook secret to use locally
- set local webhook in .env.local as
STRIPE_WEBHOOK_SECRET - now the webhooks will be sent from stripe to your computer!
Diagrams
- Diagrams can be updated here
- Copy and paste the below code, paste into editor and make changes, then update code above and here for reference
Candidate Payment Flow Markup
sequenceDiagram
StripePaymentForm->>Dreambound API: graphql/getOrCreatePaymentIntent(customerId, payPastDueOnly);
Dreambound API->>Stripe API: lookup or create stripe user;
Stripe API-->>Dreambound API: return stripe user;
Note over Dreambound API,StripePaymentForm: insert pending StripePayment with paymentAmount, chargeIds, stripeCustomerId, etc;
Dreambound API-->>StripePaymentForm: getOrCreatePaymentIntent returns clientSecret, paymentAmount;
StripePaymentForm->>Stripe API: customer submits payment;
Stripe API-->>StripePaymentForm: redirects to return url /dashboard/make-payment;
Stripe API-->>Dreambound API: api/stripe/webhook/handleStripePaymentIntentSuccess;
Note over Dreambound API,Stripe API: StripePayment looked up & marked as paid, <br /> InvoicePayment inserted <br /> InvoiceCharges marked as paid;
Dreambound API-->>StripePaymentForm: Payments table is updated;
External Payment Flow Markup
sequenceDiagram
External Stripe Payment->>Stripe API: customer submits payment;
Stripe API->>Dreambound API: api/stripe/webhook/stripeCheckoutCompleteWebhook;
Note over Dreambound API,Stripe API: Dreambound customer is looked up by email <br /> IF NOT FOUND, NO FURTHER ACTION TAKEN;
Dreambound API-->>Stripe API: lookup or create stripe user;
Stripe API-->>Dreambound API: return stripe user;
Note over Dreambound API,Stripe API: StripePayment inserted, InvoicePayment inserted;
Dreambound API-->>Dreambound Web: Payments table updated;
Changelog
- July 15, 2022: Changelog start.