Skip to main content

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
  • /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
  • /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.

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

  1. get access to a dreambound stripe developer account
  2. full instructions here
  3. login via cli stripe login
  4. important: instead of just stripe listen, use stripe listen --forward-to localhost:3000/api/stripe/webhook
  5. you will be given a webhook secret to use locally
  6. set local webhook in .env.local as STRIPE_WEBHOOK_SECRET
  7. 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.