Add Unit Tests to Your Payments Service
Build a test-writing playbook for payment processing and cover charge flows, refunds, and webhooks.
Write a payments test playbook
Connect GitHub, Stripe, and Sentry. The test-writing process follows a playbook, a reusable, named set of steps that encodes your team's testing conventions. Payment code has unique concerns: idempotency, currency precision, gateway retries, and PCI-safe mocking.
The playbook defines the mocking strategy, forbidden actions, and assertion style so every test file looks like it was written by the same engineer.
Playbook: !payments-tests
For payment modules:
1. Read the target module and identify exported functions.
2. Study existing tests in src/services/__tests__/ for patterns.
3. Create a test file named {Module}.test.ts next to the source.
4. Use AAA pattern (Arrange, Act, Assert).
5. Mock Stripe SDK with jest.mock('stripe'), never call real APIs.
6. Mock database calls with helpers from src/test/helpers.ts.
7. Use integer cents for all currency values.
8. Test idempotency: same key twice must not duplicate charges.
9. Cover success, error, and edge cases for every function.
10. Run npm test -- --coverage and verify 90%+ line coverage.
Forbidden:
- Real payment gateway calls.
- Floating-point arithmetic for currency.
- Modifying source to make testing easier.
- Skipping idempotency or retry edge cases.Add your best existing test file as a memory entry so Opulent has a concrete example of your team's style. It learns the assertion patterns and naming conventions from that example.
Find untested payment code
Before pointing Opulent at specific files, ask it to run the coverage report and list the payment modules with the biggest gaps. This gives a prioritized backlog of test-writing work.
Run: npm test -- --coverage Gaps under src/services/: | File | Lines | Untested functions | |-------------------------------|-------|--------------------| | PaymentService.ts | 34% | processCharge, issueRefund, handleWebhook | | SubscriptionService.ts | 41% | renewSubscription, cancelTrial, proratePlan | | InvoiceService.ts | 52% | generateInvoice, applyPromoCode, calculateTax | | PayoutService.ts | 58% | initiateTransfer, reconcileSettlement | Start with PaymentService.ts.
Run the test-writing session
Opulent reads PaymentService.ts, studies existing tests, writes the new test file following the playbook, and runs the tests. It iterates until coverage and assertions pass.
The output is a PR with the test file and a coverage summary. The tests mock the Stripe SDK, use integer cents, and cover duplicates, partial refunds, webhook signature verification, and gateway timeouts.
PASS src/services/__tests__/PaymentService.test.ts
PaymentService
processCharge
✓ charges a valid card and returns a receipt
✓ uses integer cents to avoid floating-point errors
✓ rejects duplicate charges with same idempotency key
✓ retries on Stripe gateway timeout up to 3 times
✓ throws InsufficientFundsError for declined cards
issueRefund
✓ refunds full amount for a completed charge
✓ refunds partial amount in cents when specified
✓ prevents refund exceeding the original charge amount
✓ throws AlreadyRefundedError on duplicate attempts
handleWebhook
✓ processes charge.succeeded events
✓ processes charge.refunded events
✓ verifies Stripe webhook signature
✓ ignores unrecognized event types
Coverage: 94% lines | 91% branches | 100% functionsRoll out to other payment modules
Review the first PR. If the mock strategy or assertion style is off, update the playbook before running it on more modules. Then start a run for each remaining module in parallel.
For recurring maintenance, schedule a weekly run that finds payment modules whose coverage has dropped below the threshold and writes tests for them automatically.
Sharpen the payment test loop
When a new bug appears in payments that the tests did not catch, add a regression test and update the playbook to prevent the same class of bug in the future. For example, if a webhook is not verified, add 'verify webhook signature for every webhook handler' to the playbook.
The natural chain: when the tests reveal a bug, use Debug a Bug Report End-to-End; when the payment flow needs a new feature, use Implement Bookings API from OpenAPI Spec; when coverage is part of a broader quality push, use Auto-Fix PR Bugs Before Merge.