Reputation: 6514
I'm currently migrating my app from using the Stripe Charges API to use the Stripe PaymentIntents API, in order to comply with SCA regulations.
My subscription creation code roughly looks like this:
Map<String, Object> srchOpts = new HashMap<>();
srchOpts.put("email", userEmail);
List<Customer> matchingCustomers = Customer.list(srchOpts).getData();
Customer customer = null;
Subscription subscription = null;
if ( matchingCustomers.isEmpty() ){
Map<String, Object> params = new HashMap<String, Object>();
params.put("email", userEmail);
params.put("payment_token", stripeToken);
customer = Customer.create(params);
}
else if (matchingCustomers.size() == 1) {
customer = matchingCustomers.get(0);
Map<String, Object> params = new HashMap<String, Object>();
params.put("source", stripeToken);
PaymentSourceCollection paymentSources = customer.getSources();
paymentSources.create(params);
}
Map<String, Object> item = new HashMap<String, Object>();
item.put("plan", planId); // e.g. my-pro-plan (no trial days)
Map<String, Object> items = new HashMap<String, Object>();
items.put("0", item);
Map<String, Object> params = new HashMap<String, Object>();
params.put("items", items);
params.put("customer", customer.getId());
params.put("off_session", false);
subscription = Subscription.create(params);
I can see on the Stripe dashboard (in test mode) that the customer is created and has the card which I specified, but the Subscription.create call fails with:
com.stripe.exception.InvalidRequestException: This customer has no attached payment source; code: resource_missing
The customer has a card set (there is only 1 so it has to be the default card), the customer creation call happens before the subscription creation call and the customer ID is being passed to the sub creation call. Is there some other parameter I need to pass in?
I've tried setting Customer.invoice_settings.default_payment_method
when the customer is being created, and that gets me past the subscription creation. Everything looks fine from the Stripe dashboard, except that I'm testing with an SCA test card, so the transaction is incomplete until the customer has authenticated further.
I need the client secret token from the response to continue, and I thought that I would get it from #Subscription.getLatestInvoiceObject().getPaymentIntentObject().getClientSecret()
but the getLatestInvoiceObject()
call is returning null
.
I'm not setting collection_method
on the Subscription object which defaults to charge_automatically
. This is what I want because the customer is on-session and so the Invoice
being null
probably makes sense, but how do I get the client secret to pass back to the frontend? The SetupIntent
object returned by the subscription response exposes a client secret, but that object is null
too.
Upvotes: 3
Views: 4090
Reputation: 2906
Subscription Invoices
will use whichever of these three payment options are available (in order of preference):
Subscription
's default_payment_method
(reference) or default_source
(reference)Customer
's invoice_settings.default_payment_method
(reference)Customer
's default_source
(note: there is no concept of a default PaymentMethod on Customers)Also worth noting is that PaymentMethods and Sources are two distinct resources. Cards (objects with ids prefixed with card_
) can be used as either type.
Upvotes: 4