Stripe Promotion Code / Coupon code checking client side checking before subscription - node.js

I am using React and Stripe payments for subscriptions. I have the checkout all set using Elements. I want to add the possibility for a user to add a promo code set up in stripe that will be added to the subscription on creation.
What i want to do is check that the promo code is valid (after checking the various subscriptions on the code) and show the affect to the customer before they commit to subscribe. I thought there would have been something built into the API but apparently not. And Stripe support desk (which is generally very good) don't don't seem to know why i want to check a code is valid and show customer it is before they actually click subscribe.
const promotionCodes = await stripe.promotionCodes.list({
active: true,
code: promoCode,
});
if (promotionCodes.data.length > 0) {
const today = Math.floor(Date.now() / 1000);
const expiry =
promotionCodes.data[0].expires_at ||
Math.floor(Date.now() / 1000) + 20;
if (today <= expiry) {
valid = true;
}
}
This is retrieving the code (there can only be 1 active result so it gets it fine). I then check its not expired. All fine, the data I cannot get is if the Coupon the Promotion code is attached to, is restricted to only certain products. For example, if i chose gold plan and try to use a code for silver plan then it would be invalid.

Related

Stripe connect onboarding test data for standard accounts

I'm trying to follow this document to setup Stripe Connect: https://stripe.com/docs/connect/enable-payment-acceptance-guide?platform=web&ui=checkout#create-account-link
At the account link phase it redirects me to Stripe for creating an account etc - for development purposes I would like to skip this instead of entering real data every time.
I came accross this document for testing Stripe: https://stripe.com/docs/connect/testing
It says there is a way to force skip but I don't see anything pop up. Is there a special value I need to pass in to enable the force skip option?
Here are the code snippets I've been using to test account linking
const stripe = new Stripe(secrets.STRIPE_SECRET_KEY, {
apiVersion: "2022-11-15"
});
export class StripeClient {
/**
* Create a Stripe account for a user. The account will be associated with the ZCal account
*/
static accountCreationRequest = async (): Promise<Stripe.Response<Stripe.Account>> => {
const account: Stripe.Response<Stripe.Account> = await stripe.accounts.create({
type: "standard"
});
return account;
};
static accountLinkRequest = async (stripeAccountId: string): Promise<Stripe.Response<Stripe.AccountLink>> => {
const accountLink: Stripe.Response<Stripe.AccountLink> = await stripe.accountLinks.create({
account: stripeAccountId,
refresh_url: `${config.CLIENT_BASE_URL}/account/integrations`,
return_url: `${config.CLIENT_BASE_URL}/account/integrations`,
type: "account_onboarding"
});
return accountLink;
};
}
The "force skip" line on testing Doc is for OAuth flow, which is unrelated to your Onboarding flow, so first let's ignore it.
When you make your first request to create the account, you can prefill as much information as you want, and it will be automatically populated in the flow later. Here is the full list of fillable fields.
You can pre-fill any information on the account, including personal
and business information, external account information, and more.

Stripe payment API: How to present Google Pay as a payment type (web)?

I'm following a guide on how to create a checkout page for a product via the stripe API. I can set the "payment_method_types", but the list of payment_method_types does not show any option for Google Pay or Apple Pay.
Edit: As mentioned in my answer, I thought the Google Pay option would just show up, but it's not showing up on the billing/checkout portal. I also checked that Google Pay is set to "on" in my Stripe account settings. I even went through the effort of creating a GPay account with valid payment info, and it still did not show up even after I logged in via google and went to the checkout page.
Code:
const checkoutSession = {
automatic_tax: true,
tax_id_collection: true,
collect_shipping_address: true,
allow_promotion_codes: true,
line_items: [selectedPrice],
success_url: window.location.href,
cancel_url: window.location.href,
metadata: {
key: 'value',
},
};
checkoutSession.mode = 'subscription';
checkoutSession.payment_method_types = ['card'];
const docRef = await db
.collection('customers')
.doc(firebase.auth().currentUser.uid)
.collection('checkout_sessions')
.add(checkoutSession);
// Wait for the CheckoutSession to get attached by the extension
docRef.onSnapshot((snap) => {
console.log("entered Snap: ", snap.data());
const { error, url } = snap.data();
if (error) {
// Show an error to your customer and then inspect your function logs.
alert(`An error occured: ${error.message}`);
document.querySelectorAll('button').forEach((b) => (b.disabled = false));
}
if (url) {
window.location.assign(url);
}
});
EDIT: THIS DOESN'T WORK. Original answer below.
Found the answer
When you turn on cards in the Dashboard, it enables Apple Pay and
Google Pay too. When you enable automatic_payment_methods or pass card
in payment_method_types on the PaymentIntent, your customer also sees
Apple Pay and Google Pay. These payment methods require you to serve
your application over HTTPS in development and production.
Additionally, you need to verify your domain for Apple Pay.
Stripe only displays Apple Pay and Google Pay on their corresponding
platforms. Customers also need an active card associated with their
account.

Creating Stripe transfers at the same time as creating Payment intent

I'm using Stripe and am trying to implement the scenario described here
The frontend is making a call to the backend that has this code
var service = new PaymentIntentService();
var createOptions = new PaymentIntentCreateOptions
{
PaymentMethodTypes = new List<string>
{
"card",
},
Amount = (long?)(payment.Amount * 100),
Currency = "EUR"
};
var result = await service.CreateAsync(createOptions);
return result.ClientSecret
In the documentation it says that the below code should be run "later" but it doesn't specify when. In my case I would prefer submitting the transfers to Stripe as soon as possible and preferably connecting them to the above payment so the transfers would be automatically handled by Stripe when the payment is done.
As I read the documentation (https://stripe.com/docs/api/transfers/create#create_transfer-source_transaction) SourceTransaction could be used for this.
var transferService = new TransferService();
var transfer1Options = new TransferCreateOptions
{
Amount = 100,
Currency = "eur",
Destination = "{{CONNECTED_STRIPE_ACCOUNT_ID}}",
SourceTransaction = result.Id
};
var transfer1 = transferService.Create(transfer1Options);
The result -variable contains a Charges -list but that is empty and when doing the above, i.e. adding the payment intent's id to SourceTransaction property, I get an error that the charge does not exist.
In the documentation it says that the below code should be run "later" but it doesn't specify when
It's whenever you are ready to make the transfer, after the payment has succeeded, essentially.
As I read the documentation (https://stripe.com/docs/api/transfers/create#create_transfer-source_transaction) SourceTransaction could be used for this.
Yep, you should definitely use source_transaction here! Otherwise the transfers won't succeed since the funds would not be available yet.
The result -variable contains a Charges -list but that is empty and when doing the above, i.e. adding the payment intent's id to SourceTransaction property, I get an error that the charge does not exist.
The value to pass to source_transaction is indeed the intent.charges.data[0].id , of a PaymentIntent that has succeeded and has a Charge.
You don't have a Charge ID because you haven't actually processed a payment yet. Creating a PaymentIntent doesn't process a payment.
You have to do the normal full PaymentIntent integration by building a form that collects payment details and 'confirms' the PaymentIntent, that's the action that processes the payment, and after that succeeds, the charges array will have a Charge in it that can be used for the transfer. For example you might create the transfer as part of handling the payment_intent.payment_succeeded webhook event.
https://stripe.com/docs/payments/accept-a-payment?platform=web&ui=elements

Stripe update subscription and add new payment method with 3d secure

I created a subscription as described on https://stripe.com/docs/billing/subscriptions/elements but now I want to give the user the choice to change the plan on the subscription and use another payment method, ex 3d Secure card. However if I update the subscription to get a client secret for a new payment intent as following:
func (c *Client) UpdateSubscription(s *models.Subscription) (*models.Subscription, error) {
sps := &stripe.SubscriptionParams{
DefaultPaymentMethod: stripe.String(s.PaymentMethodId),
CancelAtPeriodEnd: stripe.Bool(false),
ProrationBehavior: stripe.String(string(stripe.SubscriptionProrationBehaviorAlwaysInvoice)),
}
if s.CreatePaymentIntent {
s.PaymentBehavior = "allow_incomplete"
sps.PaymentBehavior = stripe.String(s.PaymentBehavior)
sps.AddExpand("latest_invoice.payment_intent")
} else if s.ItemID != "" {
sps.Items = []*stripe.SubscriptionItemsParams{
{Price: stripe.String(s.PriceID)},
{ID: stripe.String(s.ItemID), Deleted: stripe.Bool(true)},
}
}
ss, err := sub.Update(s.ID, sps)
if ss.LatestInvoice != nil && ss.LatestInvoice.PaymentIntent != nil {
s.PaymentIntentClientSecret = ss.LatestInvoice.PaymentIntent.ClientSecret
}
return s, err
}
the PaymentIntentClientSecret is the same for the subscription which means it is already processed.
Stripe 'confirm card' API is throwing error payment_intent_unexpected_state https://stripe.com/docs/error-codes/payment-intent-unexpected-state and that's probably because I used that payment intent previously to create the subscription. However I still need a new payment intent to authorise the new card.
What is the status of the PaymentIntent that you are getting these errors on?
It looks like you are properly updating the Subscription’s Price and PaymentMethod as well as creating a new Invoice for this proration. If that is the case, the Invoice may automatically be getting paid on that update. So this error might be happening because you are trying to confirm a PaymentIntent that has already successfully charged your customer.
I think it would be good to check the status of the LatestInvoice’s PaymentIntent when you get the updated Subscription object in your function.
If the status is succeeded you can simply send the customer back that updating their subscription was successful.
If the status is requires_action you should send the PaymentIntent’s client secret back to your client side and call handleCardAction[1] to allow your customer to resolve whatever action may need to be taken (3DS Auth, etc)
If the status is requires_payment_method the new card was declined and you should have your customer input new credit card info to try to update the subscription’s PaymentMethod again.
[1]https://stripe.com/docs/js/payment_intents/handle_card_action

Razorpay subscription handler not sending response.razorpay_subcription_id and response.razorpay_signature

I am trying to implement a model in which if a user pays for the first time he is registered with the subscription according to plan he selects.
I am using ReactJs as forntend and NodeJs as backend.
There are 2 issues that I am currently facing -
Not getting razorpay_subscription_id and razorpay_signature -
This is the Nodejs code for creating a subscription and returning subscriptionId for further process.
const plans = await instance.plans.all();
let i;
for (i = 0; i < plans.count; i++) {
if (plans.items[i].item.name === "Test plan - Weekly") break;
}
const planId = plans.items[i].id;
const subscriptionData = {
plan_id: planId,
total_count: 1,
quantity: 1,
customer_notify: 1,
};
const response = await instance.subscriptions.create(subscriptionData);
res.send(response.id);
This is my react code to open razorpay for payment. When this opens I do get an error in console, which is serviceworker must be a dictionary in your web app manifest.
Note - I have replaced key below due to security reasons.
axios.get("http://localhost:5000/razorpay").then((subscriptionId) => {
var options = {
key: "Test_Key",
subscription_id: subscriptionId,
name: "Consuma",
description: "Monthly Test Plan",
handler: function (response) {
console.log(response);
},
};
var razorpay = new window.Razorpay(options);
razorpay.open();
razorpay.on("payment.failed", function (response) {
alert(response.error.code);
alert(response.error.description);
alert(response.error.source);
alert(response.error.step);
alert(response.error.reason);
alert(response.error.metadata.order_id);
alert(response.error.metadata.payment_id);
});
});
I should expect razorpay_subscription_id and razorpay_signature for verification, but those fields are missing.
Below is the response that I get.
checkout_logo: "https://cdn.razorpay.com/logo.png"
custom_branding: false
org_logo: ""
org_name: "Razorpay Software Private Ltd"
razorpay_payment_id: "pay_HS0u4wIhrQavw
Payment not being captured and not considered as subscription payment
The plan that I used above is of Rs. 699 to be charged weekly, but while paying the amount I am only asked to pay Rs. 1. According to Razorpay documentation this rs 1 is an authorization from the user to subscribe to the plan and once this is successful, the plan amount will be deducted from users account as per the plan. But nothing such happens. Also, in the Razorpay dashboard this payment is considered as authorized and not as captured.
So, the first issue was an error in code. I wasn't sending a correct subscription id and due to that I wasn't receiving any expected response.
As far as 'serviceworker' error is considered I received following response fro razorpay team.

Resources