# Migrate Stripe Charges to PaymentIntents
`stripe.charges.create` is deprecated. If you inherit it, migrate; do not extend. The mapping is mechanical, but every call site needs the full replacement, not a rename.
## Parameter map
- `amount` and `currency`: same on PaymentIntents.
- `source` (token or card ID): becomes `payment_method`. If the code passes raw card tokens, switch to PaymentMethods created client-side.
- `capture: false` (auth then capture): becomes `capture_method: 'manual'`, then a separate `paymentIntents.capture` call. The verify-before-capture discipline still applies.
- `application_fee_amount` and `transfer_data[destination]`: same shape on PaymentIntents for Connect destination charges.
- `description`, `metadata`, `statement_descriptor`: same.
## The migration order
1. Inventory every `charges.create` call and every `charge.succeeded` webhook handler. Do not start rewriting until the list is complete.
2. Replace creation with `paymentIntents.create` plus `confirm`. Server-side confirmation is fine for trusted flows; client-side confirmation is required when 3DS might trigger.
3. Add `requires_action` handling at every confirmation site. The old Charges flow never had this step; skipping it is the number one migration bug.
4. Switch webhooks: `charge.succeeded` becomes `payment_intent.succeeded`. Keep both handlers live during the transition if old charges can still settle.
5. Update the test suite to force the `requires_action` path with 3DS test cards.
6. Remove the Charges code only after the last charge created under the old path has settled or been refunded.
## What not to do
- Do not "migrate" by wrapping charges.create in a helper that still calls the deprecated API. The deprecation is the point.
- Saved cards: migrate to SetupIntents for storing payment methods, not zero-amount charges.