Giter VIP home page Giter VIP logo

stripe-samples / checkout-single-subscription Goto Github PK

View Code? Open in Web Editor NEW
712.0 15.0 330.0 5.48 MB

Learn how to combine Checkout and Billing for fast subscription pages

Home Page: https://checkout.stripe.dev/?mode=subscription

License: MIT License

HTML 4.76% CSS 27.35% JavaScript 6.45% Java 6.76% PHP 9.60% Python 5.34% Ruby 24.64% C# 7.54% Go 5.95% Hack 1.60%
stripe-checkout stripe-billing stripe

checkout-single-subscription's Issues

Domain redirect

var redirectToCheckout = function(planId) {
// Make the call to Stripe.js to redirect to the checkout page
// with the current quantity
stripe
.redirectToCheckout({
items: [{ plan: planId, quantity: 1 }],
successUrl:
"https://" + DOMAIN + "/success.html?session_id={CHECKOUT_SESSION_ID}",
cancelUrl: "https://" + DOMAIN + "/canceled.html"
})
.then(handleResult);
};

MISS protocol in default client only redirect (in javascript)

Discrepancy between GitHub code and live-demo code — intentional?

Hello, I'm working through the client-only integration, and noticed a discrepancy between the live demo, at https://4iupj.sse.codesandbox.io/ , and the GitHub repo.

If you inspect the live demo in browser devtools, there's an extra code block, which results in the session information being displayed on the 'success' page (copied at the bottom).

That fetch path doesn't exist in the sample or on my own page. Am I correct in assuming that

  1. it's just there to demo what the session returns? And,
  2. Checkout does not generate/provide such a path that I would be able to access? I.e., is this just a path or route you guys made on your own server for the purposes of the demo?

I'm getting all the necessary logs and session info in my own server side code via a Webhook, but I wanted to make sure that I'm not missing a more direct way to acquire the session info on the client side.

Discrepant Code

var sessionId = urlParams.get("session_id")
if (sessionId) {
   fetch("/checkout-session?sessionId=" + sessionId).then(function (result) {
          return result.text();
        }).then(function (session) {
          var sessionJSON = JSON.stringify(session, null, 2);
          document.querySelector("#stripejson").textContent = sessionJSON;
        })
      }

Thanks!

Can't redirect to Localhost on success, preventing local testing

Hello, the client-only integration is great!
I'm running into a bug (maybe?) when attempting to test this locally.

To reproduce

  1. change the successURL: to "https://localhost:5000" + "?session_id={CHECKOUT_SESSION_ID}",
  2. go through checkout successfully, using a Test Card
  3. see error in browser This site can’t provide a secure connectionlocalhost sent an invalid response. ERR_SSL_PROTOCOL_ERROR

This seems to be a bug, because when loading Checkout locally, it simply issues a warning in the console You may test your Stripe.js integration over HTTP. However, live Stripe.js integrations must use HTTPS.

Current Workaround
I've created a live testing environment, but this is extremely slow to test, and hard to debug via logging, because any change requires a redeploy. Being able to use Localhost would
greatly speed up the dev process, on both counts (speed and readability).

Question
If this is an intentional feature, is there a recommended workaround to serve Localhost over https? (I did due-diligence Google this, but most solutions are more involved than someone using the client-only integration is probably comfortable with! So just checking here, first.)

Additional Information
Not sure if it's relevant, but I'm using the Firebase local emulator to locally serve and test my web app.

Thanks!

Client-only in codesandbox.io

Hello,
I would like to set up a codesandbox.io client only example to share it.
I guess the server part with the start command in the package.json would not be needed.
Can you help me on that one ?

Stripe

Bug report

Describe the bug

A clear and concise description of what the bug is.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior

A clear and concise description of what you expected to happen.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: [e.g. macOS, Windows]
  • Browser (if applies) [e.g. chrome, safari]
  • Server environment [e.g. Go, Java, Node, PHP, Python, Ruby, TypeScript]

Additional context

Add any other context about the problem here.

No StripeOptions class

Bug report

Sample Startup.cs Breaks

The sample Startup.cs code references a type that does not exist in the namespaces or libraries referenced in the using statements.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Copy the code into an actual file and see the error

Expected behavior

A clear and concise description of what you expected to happen.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: [e.g. macOS, Windows]
  • Browser (if applies) [e.g. chrome, safari]
  • Server environment [e.g. Go, Java, Node, PHP, Python, Ruby, TypeScript]

Additional context

Add any other context about the problem here.

The client

Bug report

Describe the bug

How to update the home page with Stripe.

I changed the value in .env file, but the price in home page don't change

Is it normal ? if yes what can I do ?

PS : When I click on the price button the redirection works and the price in payement page is good, but not in home page.

Stripe session customer info not being retrieved despite successful webhook

Hi there,
I have the following code taken from multiple flask subscription docs on the web. Everything works perfectly fine, however, stripeData = event["data"]["object"]looks like its not returning the customer info, as "didn't get stored" gets return in the success endpoint. I am so confused why this is the case when the web hook is successful per the payments going through in the dashboard and when connecting to stripe-cli, the customer info even gets shown. However, by its own it doesn't. Would really appreciate some clarity.

stripe_keys = {
    "secret_key":'sk_test_5...GKIv8T67w0f8000ypWZx9om',
    "publishable_key": 'pk_test_51Hhm...X6q2FLqW',
    "price_id": 'price_1HhmCyBBlU..f',
    "endpoint_secret":'whsec_lFkitrmzG...u0PbD4riv7'
}
stripe.api_key = stripe_keys["secret_key"]

@blueprint.route("/stripe_webhook", methods=["POST"]) 
def stripe_webhook():
    # print("Webhook called ")
    payload = request.get_data(as_text=True)
    sig_header = request.headers.get("Stripe-Signature") 
    event = None
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, stripe_keys["endpoint_secret"]
        )
    except ValueError as e:
        return "Invalid payload", 400
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return "Invalid signature", 400

    if event["type"] == "checkout.session.completed":
        try:
            stripeData = event["data"]["object"]
            stripe_customer_id = stripeData['customer']
            stripe_subscription_id = stripeData['subscription']
            user = User.query.filter_by(id=str(session['id'])).first()
            user = User(stripeSubscripstionId= stripe_subscription_id, stripeCustomerId= stripe_customer_id)
            db.session.add(user)
            db.session.commit()
        except:
            print('FAILED TO WORK') #doesn't get returned
    return {}

@blueprint.route("/config")
def get_publishable_key():
    stripe_config = {"publicKey": stripe_keys["publishable_key"]}
    return jsonify(stripe_config)

@blueprint.route("/success")
def success():
    print('session id is')
    print(session['id'])
    user = User.query.filter_by(id=str(session['id'])).first()
    if user.stripeSubscriptionId:
        return render_template("/General/success.html")
    return "didn't get stored"

@blueprint.route("/create-checkout-session") 
def create_checkout_session():
    domain_url = "http://localhost:5000/"
    stripe.api_key = stripe_keys["secret_key"]
    # User.query.filter_by(id=session['id']).first() 
    try:
        checkout_session = stripe.checkout.Session.create(

            client_reference_id=User.id,
            success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
            cancel_url=domain_url + "cancel", 
            payment_method_types=["card"],
            mode="subscription",
            line_items=[
                {
                    "price": stripe_keys["price_id"],
                    "quantity": 1,
                }
            ]
        )
        return jsonify({"sessionId": checkout_session["id"]})
    except Exception as e:
        return jsonify(error=str(e)), 403

System information

  • OS: macOS BigSur
  • Browser (if applies) Safari
  • Server environment py 3.9

Checkout page attaches Payment Method to customer even if card data is incorrect.

Bug report

Describe the bug

Checkout page in 'subscription' mode saves payment methods even when payment is not sucessfull (declined card for example).
This may not be a bug in a strict sense but it is a major inconvience, because if user enters wrong card data by mistake it will be later seen in his payment methods without any indication that it is wrong card.

After inspecting events generated by stripe I can see that there is no setup_intent.* events present in logs. So it seems like Checkout in subscription mode doesn't use SetupIntent API to create payment methods as is recommended in docs (https://stripe.com/docs/api/payment_methods/attach). When I used SetupIntent API to manually add a payment method to a customer it is not created if card data is incorrect, which is desirable behaviour.

I understand that it may be a design decision to make it work that way, but it is unituitive from customer's view point.

To Reproduce

  1. Create customer
  2. Create Checkout Session in subscription mode with added customer field and payment_method_types set to ['card']
  3. Go to Checkout page
  4. Enter wrong card data ( for example 4000000000000002 from test cards list: https://stripe.com/docs/testing#cards-responses)
  5. Enter correct card (for example 4242424242424242 from test cards list)
  6. Go to dashboard or customer portal to see that coresponding customer has failed payment method attached to his account.

Expected behavior

I expected that Checkout page wouldn't add payment methods to customer if first subscription payment has failed.

Screenshots

System information

Additional context

How to backdate and postdate the start of a subscription with the new checkout?

Hello. We need to handle these 2 scenarios:

  1. Postdate a subscription: save the card info, charge the first payment instantly, but only start the subscription in the future.
    E.g, user pays first payment on Dec 1 2020, but subscription only starts on Jan 1 2021, and the next payment will be on Jan 1 2022.

  2. Backdate a subscription: save the card info, charge the first payment instantly, starts the subscription in the past.
    E.g, user pays first payment on Feb 1 2020, but subscription actually started on Jan 1 2020, and the next payment will be on Jan 1 2021.

With the old legacy charge/subscription API, we were able to achieve these using one time charges and set future start date for subscription. These cases are very common in membership payments that do not offer proration (proration in full).

How can we address these cases? Thank you!

Repeat Orders

Every night, 3 or so orders repeat. Any idea how this could be happening?

Screen Shot 2021-07-07 at 4 55 57 PM

Using stripe samples and client-server means you need to change the STATIC_DIR

When using the stripe samples command to clone the repository, it removes the python/ folder (and copies the content into 'server'). This then means that when you run the application, it is unable to find the client files because the static dir is set to ../../client while it should be set to ../client.

Potential solutions:

  • Ensure the stripe samples command also edits the .env.example file to set the static director appropriately
    OR
  • Update the documentation to make clear that the static dir should be updated.

Readme intro - error with prices create commant

Bug report

Describe the bug

The stock command in the readme.md gives an error:
Missing required param: Currency

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:
Follow the readme.md , Using Node although it would be the same for others.

When you are up to this part:

./stripe prices create
  -d "product=ID_OF_BASIC_PRODUCT"
  -d "unit_amount=1800"
  -d "currency=usd"
  -d "recurring[interval]=month"

After substituting in your ID, you'll get this error:

  "error": {
    "code": "parameter_missing",
    "doc_url": "https://stripe.com/docs/error-codes/parameter-missing",
    "message": "Missing required param: currency.",
    "param": "currency",
    "request_log_url": "https://dashboard.stripe.com/test/logs/req_DizJci51DGcUB5?t=1685198228",
    "type": "invalid_request_error"
  }
}

Expected behavior

That the price is created, without you having to go in the GUI and do it.

PHP Demo's "shared.php" file missing

Many of the files in the server/PHP demonstration of this repo are including a "shared.php" file, but there is not one anywhere in the repo.

Some of the files that include it are:
server/php/public/config.php
server/php/public/get-checkout-session.php
server/php/public/webhook.php
server/php/public/create-checkout-session.php

How to make a referral program with stripe subscription

Hi there,
My question is more business related than code:

In my app, I'd like to give an extra week of subscription if a user referred someone to the app.
My question:

  1. Does stripe provide any referral program apis? to handle the referral program backend by itself just like with checkout. I wasn't able to find any.
  2. if not, I would construct the program, in such logic on my own end:
    Attach the inviter's username to the end of the URL, if the invitee logs in, a week gets taken of from inviter's subscription time in db. My only problem is that how will I make this manual update to the user's stripe info on your db?

Examples needed fixing for legibility and Windows environments

Bug report

I've added a pull request #218 to fix a few issues with the examples documentation, especially when using on Windows environments. The formatting was also fairly inconsistent, and I feel the updates I did makes it more legible.

Welcome any changes of course.

Cannot destructure property 'subscription' of 'undefined' as it is undefined at handlePaymentMethodRequired

Hi,

I have been trying to implement this checkout-single-subscription

What is working :

  • Integration Card : 4242424242424242
  • Integration Card : 4000002760003184 (3DS)

Failure :

  • Integration Card : 4000008260003178.

** Expected ** : retrieve the error message insufficient funds

** Explanation ** : The logs (see index) are able to throw the right error Your card has insufficient funds. However the error catching in the creatsubscription function fails.

Catching 8 in createSubscription = TypeError: Cannot destructure property 'subscription' of 'undefined' as it is undefined.
at handlePaymentMethodRequired (Checkoutform.js:103)

I had to create a default error showCardError({ error: { message: 'Your card was declined.' } });. because I wasn't able to use the thrown erros.

Screenshot 2020-06-24 at 12 57 51

export default function CheckoutForm(props) {
    const [priceId, setPriceId] = React.useState('price_1GqywBC2ahMs23DqyBiAZLZG');
    const [openedSuccessModal, setOpenedSuccessModal] = React.useState(false)
    const[openErrorSnack,setOpenSnack] = React.useState(false)
    const[errorMessage,setErrorContent] = React.useState('')

  const stripe = useStripe();
  const elements = useElements();



function showCardError(event) {
  console.log("showCardError")
  if (event.error) {
    setErrorContent(event.error.message)
    setOpenSnack(true)
  } else {
    setErrorContent('event.error.message')

  }
}

function retryInvoiceWithNewPaymentMethod(
  customerId,
  paymentMethodId,
  invoiceId,
  priceId
) {
  console.log("retryInvoiceWithNewPaymentMethod")
  return (
    fetch('http://127.0.0.1:8000/api/user/retry_subscription/', {
      method: 'post',
      headers: {
        'Content-type': 'application/json',
          Authorization: `Bearer ${props.token}`
      },
      body: JSON.stringify({
        customerId: customerId,
        paymentMethodId: paymentMethodId,
        invoiceId: invoiceId,
      }),
    })
      .then((response) => {
        return response.json();
      })
      // If the card is declined, display an error to the user.
      .then((result) => {
        if (result.error) {
          console.log("1 throwing in retryInvoiceWithNewPaymentMethod")
            console.log("1 result=",result)
          // The card had an error when trying to attach it to a customer.
          throw result;
        }
        return result;
      })
      // Normalize the result to contain the object returned by Stripe.
      // Add the addional details we need.
      .then((result) => {
        return {
          // Use the Stripe 'object' property on the
          // returned result to understand what object is returned.
          invoice: result,
          paymentMethodId: paymentMethodId,
          priceId: priceId,
          isRetry: true,
        };
      })
      // Some payment methods require a customer to be on session
      // to complete the payment process. Check the status of the
      // payment intent to handle these actions.
      .then(handleCustomerActionRequired)
      // No more actions required. Provision your service for the user.
      .then(onSubscriptionComplete)
      .catch((error) => {
        console.log(" catching 2 in retryInvoiceWithNewPaymentMethod :",error)
        // An error has happened. Display the failure to the user here.
        // We utilize the HTML element we created.
        showCardError(error);
      })
  );
}





function handlePaymentMethodRequired({
  subscription,
  paymentMethodId,
  priceId,
}) {
  console.log("handlePaymentMethodRequired")
  if (subscription.status === 'active') {
    console.log("considered active in handlePaymentMethodRequired")
    // subscription is active, no customer actions required.
    return { subscription, priceId, paymentMethodId };
  } else if ( subscription.latest_invoice.payment_intent.status === 'requires_payment_method') {
    console.log("subscription.latest_invoice.payment_intent.status=",subscription.latest_invoice.payment_intent.status)
    // Using localStorage to manage the state of the retry here,
    // feel free to replace with what you prefer.
    // Store the latest invoice ID and status.

    // faire des vraies variables de ça, car comme ça = de la merde
    localStorage.setItem('latestInvoiceId', subscription.latest_invoice.id);
    localStorage.setItem('latestInvoicePaymentIntentStatus', subscription.latest_invoice.payment_intent.status);
        console.log("11 throwing in handlePaymentMethodRequired")
      console.log("11 custom_throw=",{ error: { message: 'Your card was declined.' } })



    throw { error: { message: 'Your card was declined.' } };
  } else {
    return { subscription, priceId, paymentMethodId };
  }
}


function handleCustomerActionRequired({
  subscription,
  invoice,
  priceId,
  paymentMethodId,
  isRetry,
}) {
      console.log("handleCustomerActionRequired")

  if (subscription && subscription.status === 'active') {
        console.log("subscription exists and subscription.status ===active in handleCustomerActionRequired")

    // Subscription is active, no customer actions required.
    return { subscription, priceId, paymentMethodId };
  }

  // If it's a first payment attempt, the payment intent is on the subscription latest invoice.
  // If it's a retry, the payment intent will be on the invoice itself.
  let paymentIntent = invoice ? invoice.payment_intent : subscription.latest_invoice.payment_intent;

  if (paymentIntent.status === 'requires_action' ||  (isRetry === true && paymentIntent.status === 'requires_payment_method')) {
    console.log("paymentIntent.status:",paymentIntent.status)
    console.log("paymentIntent.client_secret:",paymentIntent.client_secret)

    return stripe
      .confirmCardPayment(paymentIntent.client_secret, {
        payment_method: paymentMethodId,
      })
      .then((result) => {
        if (result.error) {
          console.log("4")
          console.log("Throwing 4 in handleCustomerActionRequired =",result)
          // Start code flow to handle updating the payment details.
          // Display error message in your UI.
          // The card was declined (i.e. insufficient funds, card has expired, etc).
          console.log("result ")
          throw result;
        } else {
          if (result.paymentIntent.status === 'succeeded') {
            console.log("result.paymentIntent.status:",result.paymentIntent.status)
            console.log("in handleCustomerActionRequired")
            // Show a success message to your customer.

            setOpenedSuccessModal(true)
            localStorage.removeItem('latestInvoiceId')
            localStorage.removeItem('latestInvoicePaymentIntentStatus')


            // There's a risk of the customer closing the window before the callback.
            // We recommend setting up webhook endpoints later in this guide.
            return {
              priceId: priceId,
              subscription: subscription,
              invoice: invoice,
              paymentMethodId: paymentMethodId,
            };
          }
        }
      })
      .catch((error) => {
        console.log("6")
        console.log("Catching 6 in handleCustomerActionRequired=",error)
        showCardError(error);
      });
  } else {
    // No customer action needed.
    return { subscription, priceId, paymentMethodId };
  }
};


  function onSubscriptionComplete(result) {
    console.log("onSubscriptionComplete")
    console.log("result.subscription.status=",result.subscription.status)
    if (result.subscription.status === 'active') {

      setOpenedSuccessModal(true)
      localStorage.removeItem('latestInvoiceId')
      localStorage.removeItem('latestInvoicePaymentIntentStatus')

  }
}


  function createSubscription({ customerId, paymentMethodId, priceId }) {
  return (
    fetch('http://127.0.0.1:8000/api/user/create_subscription/', {
      method: 'post',
      headers: {
        'Content-type': 'application/json',
          Authorization: `Bearer ${props.token}`

      },
      body: JSON.stringify({
        customerId: customerId,
        paymentMethodId: paymentMethodId,
        priceId: priceId,
      }),
    })
      .then((response) => {
        return response.json();
      })
      // If the card is declined, display an error to the user.
      .then((result) => {
        if (result.error) {
                            console.log("7 throwing in createSubscription ")
                              console.log("7 result=",result)

          // The card had an error when trying to attach it to a customer.
          throw result;
        }
        return result;
      })
      // Normalize the result to contain the object returned by Stripe.
      // Add the addional details we need.
      .then((result) => {
          //console.log("return=",result)
        return {
          paymentMethodId: paymentMethodId,
          priceId: priceId,
          subscription: result,
        };
      })

      // Some payment methods require a customer to be on session
      // to complete the payment process. Check the status of the
      // payment intent to handle these actions.
      // // If attaching this card to a Customer object succeeds,
      // // but attempts to charge the customer fail, you
      // // get a requires_payment_method error.
      // // No more actions required. Provision your service for the user.
        .then(handleCustomerActionRequired)
        .then(handlePaymentMethodRequired)
        .then(onSubscriptionComplete)
        .catch((error) => {
        // An error has happened. Display the failure to the user here.
        // We utilize the HTML element we created.
        console.log("8")
        console.log("Catching 8 in createSubscription =",error)
        showCardError({ error: { message: 'Your card was declined.' } });
      })
  );
}

  function createPaymentMethod(cardElement, customerId, priceId) {
  return stripe
    .createPaymentMethod({
      type: 'card',
      card: cardElement,
    })



    .then((result) => {
      if (result.error) {
        console.log("9 in createPaymentMethod")
        showCardError(result)
      } else {
          let latestInvoiceId = localStorage.getItem('latestInvoiceId');
        let latestInvoicePaymentIntentStatus =  localStorage.getItem('latestInvoicePaymentIntentStatus')

        if (latestInvoiceId && latestInvoicePaymentIntentStatus==='requires_payment_method'){
            retryInvoiceWithNewPaymentMethod(customerId, result.paymentMethod.id, latestInvoiceId,priceId)
        }

        else  createSubscription({
          customerId: customerId,
          paymentMethodId: result.paymentMethod.id,
          priceId: priceId,
        });
      }
    });
}



  const handleSubmit = async (event) => {
    // We don't want to let default form submission happen here,
    // which would refresh the page.
    event.preventDefault();

    console.log("priceId=",priceId)
    console.log("customerId=",props.customerId)

    if (!stripe || !elements) {
      // Stripe.js has not yet loaded.
      // Make sure to disable form submission until Stripe.js has loaded.
      return;
    }

      await createPaymentMethod(elements.getElement(CardElement),props.customerId,priceId)
   };



  return (
    <form onSubmit={handleSubmit}>
      <FormControl component="fieldset">
  <FormLabel component="legend">Plan</FormLabel>
  <RadioGroup aria-label="product_id" name="product_id" value={priceId} onChange={(event)=>setPriceId(event.target.value)}>
    <FormControlLabel value="price_1GvhOFC2ahMs23Dqf7E83kTk" control={<Radio />} label="EXPENSIVE Plan" />
    <FormControlLabel value="price_1GqywBC2ahMs23DqyBiAZLZG" control={<Radio />} label="Veterinary Plan (15.99 / mois)" />
  </RadioGroup>
</FormControl>
      <CardSection/>
      <button disabled={!stripe}>Save Card</button>
<ModalSuccessPayment is_open={openedSuccessModal} setIsOpen={setOpenedSuccessModal} text ={<Emoji text=" 🎉️ Your payment has been accepted and your subscription started" />} {...props}/>
      {/*{snack}*/}
      <Snackbar open={errorMessage.length > 0} autoHideDuration={600000} message={errorMessage}/>
    </form>
  );
}

Subscription management

Hi there,

Thanks for the demo. Do you have any demo with subscription management (and authentication ?) using the hosted checkout page?

Create new customers all the time when a subscription is created

I am trying out Stripe checkout client-only integration. I am facing an issue which actually creates a new customer after checkout even though a customer with the same email id exists. Is there a way to handle it in a way that if a customer already exists with the same name add the subscription to the existing customer or create a new customer and add the subscription to the new customer

obvious mistake or is that a deliberate choice behind the screen

hey, I agree that it's an obvious mistake but is there any other valid reason or serious reason why the amount is different from the index page to the checkout page?

plan 5$ week become 5$ /month
plan 12$ week become 15$/month

I guess that this is just a parameter inside dashboard wrongly setup

billing address not stored with customer/invoice

I'm using the client-server code with python and added billing_address_collection="required", to the server-side create checkout session code. With the payment form, the billing address is shown, but it is not stored with neither the customer nor the invoice.

Change .NET Code to use App Secrets

Bug report

.NET sample shows using env variables for retrieving secrets. There's actually a more secure way recommended by Microsoft.

Describe the bug

The current example code shows the following in Startup.cs:

services.Configure<StripeOptions>(options =>
            {
                options.PublishableKey = Environment.GetEnvironmentVariable("STRIPE_PUBLISHABLE_KEY");
                options.SecretKey = Environment.GetEnvironmentVariable("STRIPE_SECRET_KEY");
                options.WebhookSecret = Environment.GetEnvironmentVariable("STRIPE_WEBHOOK_SECRET");
                options.BasicPrice = Environment.GetEnvironmentVariable("BASIC_PRICE_ID");
                options.ProPrice = Environment.GetEnvironmentVariable("PRO_PRICE_ID");
                options.Domain = Environment.GetEnvironmentVariable("DOMAIN");
 });

However, for greater security (outlined in the next link), individuals should use App Secrets to manage these things.

Example code:

POCO:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyNamespace
{
    /// <summary>
    /// Holds our Stripe secrets. 
    /// </summary>
    /// <remarks>
    /// This is established for DI.
    /// </remarks>
    public interface IStripeApiSecrets
    {
        /// <summary>
        /// The base URI for the API calls.
        /// </summary>
        /// <remarks>
        /// This is so we can change between sandbox and live via config.
        /// </remarks>
        Uri ApiBaseUri
        {
            get;
        }

        /// <summary>
        /// The publishable token.
        /// </summary>
        string PublishableKey
        {
            get;
        }

        /// <summary>
        /// The secret token.
        /// </summary>
        string SecretKey
        {
            get;
        }
    }

    /// <summary>
    /// POCO object for retrieving our secret Stripe API data.
    /// </summary>
    /// <remarks>
    /// See https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1&tabs=linux#map-secrets-to-a-poco
    /// </remarks>
    public sealed class StripeApiSecrets : IStripeApiSecrets
    {
        /// <inheritdoc/>
        public Uri ApiBaseUri
        {
            get;
            set;
        }

        /// <inheritdoc/>
        public string PublishableKey
        {
            get;
            set;
        }

        /// <inheritdoc/>
        public string SecretKey
        {
            get;
            set;
        }
    }
}

Using it in Startup.cs:

// Init our Stripe secrets for taking payments.
var stripeSecrets = Configuration.GetSection(nameof(StripeApiSecrets)).Get<StripeApiSecrets>();
if (stripeSecrets == null)
{
    throw new InvalidDataException($"{nameof(stripeSecrets)} is null.");
}
// For DI.
services.AddSingleton<IStripeApiSecrets>(stripeSecrets);

Edit User Secrets (Windows/VS2019 method shown here):

image

Enter data into JSON configuration with matching property names:

image

To Reproduce

N/A

Expected behavior

N/A

Screenshots

If applicable, add screenshots to help explain your problem.

System information

Server Env: .NET

Additional context

N/A

Use

Bug report

Describe the bug

A clear and concise description of what the bug is.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior

A clear and concise description of what you expected to happen.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: [e.g. macOS, Windows]
  • Browser (if applies) [e.g. chrome, safari]
  • Server environment [e.g. Go, Java, Node, PHP, Python, Ruby, TypeScript]

Additional context

Add any other context about the problem here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.