Back to use cases
Code Quality

Add Unit Tests to Your Payments Service

Build a test-writing playbook for payment processing and cover charge flows, refunds, and webhooks.

Playbooks
AuthorOpulent
CategoryCode Quality
FeaturesPlaybooks
start it with one message
Write unit tests for all untested functions in src/services/PaymentService.ts. Cover processCharge, issueRefund, and handleWebhook. Mock the Stripe SDK and database calls. Each function needs tests for success, error, and edge cases like duplicate charges and partial refunds. Use integer cents for currency. Target 90% line coverage.
Run this in OpulentCopy it, swap the names for your own, and send it.
connected systems
GitHubManage repositories, track code changes, and collaborate on team projects
SentryReview errors, analyze root causes, and suggest fixes for rapid issue resolution
StripeStreamline business billing, payments, and account management
step 1

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.
Tip

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.

step 2

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.
step 3

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% functions
step 4

Roll 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.

step 5

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.