I'm attempting to create a monthly subscription with free 7 day trial, but after the trial period the payment fails.
EDIT: It appears to fail because the customer has no default payment method, so despite the payment method being attached to customer, it is not set to default. I can not figure out how to set it to default payment method.
I am setting ConfirmCardSetup in frontend javascript, which I believe is tying the card to the customer. And I am creating the customer, and starting the subscription/trial in my backend django view.
I have found this in Stripe documentation:
"To use this PaymentMethod as the default for invoice or subscription payments, set invoice_settings.default_payment_method, on the Customer to the PaymentMethod’s ID."
but I am unsure how to get the Payment Method ID from front end, and use it to update the customer.
Here is my subscription create view:
class SubscriptionCreateView(View, SubscriptionCancellationMixin,
CustomerMixin, StripeReferenceMixin, FetchCouponMixin):
"""View to Create subscription and sync with stripe"""
def post(self, request, *args, **kwargs):
context = {}
subscription_payload = {}
price_id = request.POST.get('price_id')
coupon_id = request.POST.get('coupon_id')
customer = self.fetch_customer(request.user)
#Retrieve stripe coupon id or None
if coupon_id != '':
stripe_coupon_id, is_valid = self.is_coupon_valid(coupon_id)
#Set payload to create a subscription
subscription_payload['coupon'] = stripe_coupon_id
#Send invalid coupon response
if not is_valid:
context['invalid_coupon'] = True
response = JsonResponse(context)
response.status_code = 400
return response
if not customer:
customer = self.stripe.Customer.create(
email=request.user.email
)
try:
now = int(time.time())
# Cancel the previous subscription.
self.cancel_subscription(customer)
#Create a new subscription
subscription_payload.update({
'customer':customer.id,
'items': [{
'price': price_id,
},
],
'trial_end': now +30,
},
)
#create a setup intent
setup_intent = self.stripe.SetupIntent.create(
payment_method_types=["card"],
customer=customer.id,
)
subscription = self.stripe.Subscription.create(**subscription_payload)
# Sync the Stripe API return data to the database,
# this way we don't need to wait for a webhook-triggered sync
Subscription.sync_from_stripe_data(subscription)
request.session['created_subscription_id'] = subscription.get('id')
# Note we're sending the Subscription's
# latest invoice and client secret
# to the front end to confirm the payment
context['subscriptionId'] = subscription.id
context['clientSecret'] = setup_intent['client_secret']
except Exception as e:
response = JsonResponse({})
response.status_code = 400
return response
return JsonResponse(context)
And here's the relevant javascript!
var stripe = Stripe($('#public-key').val());
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
}
};
var card = elements.create("card", { style: style });
card.mount("#card-element");
//Capture modal payment button click
$(document).on('click', '#submit-payment-btn', function (e) {
e.preventDefault();
//Send an ajax call to backend to create a subscription
$(this).prop('disabled', true);
let spinnerHtml = '<div class="spinner-border text-light"></div>';
let origButtonHtml = $(this).html();
$(this).html(spinnerHtml);
$.ajax({
url: $(this).data('create-sub-url'),
type: 'POST',
data: {
csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val(),
'price_id': $(this).data('price-id'),
'coupon_id': $("#coupon_id").val()
},
success: function (result) {
if(!result.clientSecret)
{
console.log("result not okay")
window.location.href = '/'
}
else{
$('#client-secret').val(result.clientSecret);
// Confirm payment intent.
console.log('set up confirmed');
stripe.confirmCardSetup(result.clientSecret, {
payment_method: {
card: card,
billing_details: {
name: $('#cardholder-name').val(),
},
}
}).then((result) => {
if (result.error) {
alert('Payment failed:'. result.error.message);
} else {
window.location.href = '/'
}
});
}
},
error: function (result){
$('#submit-payment-btn').html(origButtonHtml);
if(result.responseJSON.invalid_coupon)
{
if($(".coupon-error").length == 0)
$('.coupon-div').append('<p class="coupon-error text-center" style="color:red">Invalid coupon code</p>')
}
else{
$('#payment-modal').modal('hide');
alert('Something Went Wrong!')
}
}
}
);
})
Thanks for any help or point in the right direction! I've been trying to solve this for hours!