PayPal's order/capture flow has the same server-authority requirements as any processor. Read Payment System Basics@markdowners/payment-system-basics first; the rules below are PayPal-specific additions, not a replacement.
Order creation and capture are two separate steps
Create the order server-side with an amount computed from your own database, never from the client — the client only ever supplies which items are being purchased, never the price.
Capture (finalize) the payment server-side after the buyer approves it in the PayPal UI — never treat "buyer clicked approve in the PayPal popup" alone as a completed payment; capture can still fail (insufficient funds, buyer account restrictions) after approval.
Verifying completion
Do not trust the client-side JS SDK's approval callback as proof of payment — call PayPal's API server-side to capture and independently verify the order status before fulfilling anything. The client callback tells you the buyer approved; it does not tell you money moved.
Store PayPal's order ID and capture ID against your internal order record so any later dispute or refund can be tied back unambiguously.
Idempotency
PayPal order creation and capture calls should carry your own idempotency key (a request-id header or equivalent) so a network retry on your end cannot create a duplicate order or double-capture a payment.
Sandbox testing
Develop against PayPal's sandbox environment with sandbox buyer/business test accounts — never point client code at live PayPal credentials during development, even for a "quick manual test."
Test the full range of buyer outcomes in sandbox: successful capture, buyer cancels before approving, capture failure after approval (simulate via sandbox negative-testing accounts where available).
Currency and amounts
PayPal requires amounts as decimal strings with a currency code, not integer minor units like some other processors — validate this conversion carefully; a silent off-by-factor-of-100 bug here is easy to introduce and easy to miss in casual testing.
Webhooks/IPN
Verify every incoming webhook (or legacy IPN) against PayPal's verification endpoint before trusting its contents — an unverified webhook payload is just an HTTP POST from an untrusted source claiming to be PayPal.
Handle capture-denied, refund, and dispute webhook events explicitly — don't build only the happy-path "completed" handler and leave the rest as silent no-ops.
Refunds and disputes
Issue refunds through PayPal's API against the specific capture ID, and record the refund in your own database in the same transaction/flow as updating order status — never let PayPal's records and yours drift out of sync.
Comments (0)
Sign in to comment. Sign in
No comments yet. Be the first to add one.