Add a description to a stripe product at checkout - c#

I am working on a C# application where the app users will purchase a subscription for their clients. Since they will buy multiple subscriptions, I need to add a description (client name) to a product so it shows up in the billing portal along with the price. My current checkout code works fine and the relevant part is here:
var options = new Stripe.Checkout.SessionCreateOptions
{
LineItems = new List<SessionLineItemOptions>
{
new SessionLineItemOptions
{
Price = priceId,
Quantity = 1
},
},
PaymentMethodTypes = new List<string>
{
"card",
},
Mode = "subscription",
SuccessUrl = string.Concat("https://", domain, "/success/", hhid, "?session_id={CHECKOUT_SESSION_ID}"),
CancelUrl = string.Concat("https://", domain),
Customer = customerId,
SubscriptionData = new SessionSubscriptionDataOptions()
{
Metadata = new Dictionary<string, string>()
{
{ "userId", userId.ToString() }
}
}
};
if (!string.IsNullOrEmpty(user.PaymentCustomerId))
options.Customer = user.PaymentCustomerId;
var service = new Stripe.Checkout.SessionService();
Stripe.Checkout.Session session = await service.CreateAsync(options);
Response.Headers.Add("Location", session.Url);
I contacted Stripe support and their response was
In this particular case we do not have an option that allow you to add a description, but you are able to add the name with price_data parameter into the Checkout, Invoice Items, and Subscription Schedule APIs.
https://stripe.com/docs/api/subscription_items/create
https://stripe.com/docs/billing/prices-guide
They provided links to the two articles which I have read and re-read and don't understand how to implement it. If anyone can help it would be greatly appreciated.

Unfortunately, the Billing Portal does not show a description for the Product.
If you want to use the Billing Portal, then you would need to specify the client name in the name for the Product e.g. [client name] Product #1, and this would require you to create a new Product and Price for every client.
You can do it in the Checkout Session creation :
https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-line_items-price_data-product_data-name
https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-line_items-price_data
Or, create the Product and Price separately :
https://stripe.com/docs/api/products/create
https://stripe.com/docs/api/prices/create

Related

Stripe - How to get billing address information in Checkout.Session

I'm trying to get the billing address from Stripe Checkout from a Webhook call.
What I'm trying to achieve is to get the information from the form in the yellow rectangle.
This is my Checkout configuration :
var options = new SessionCreateOptions()
{
CustomerEmail = user.Email,
BillingAddressCollection = "required",
ShippingAddressCollection = new SessionShippingAddressCollectionOptions
{
AllowedCountries = new List<string>
{
"FR",
},
},
PaymentMethodTypes = new List<string>() {
result.Payment.Type
},
LineItems = new List<SessionLineItemOptions>{
new SessionLineItemOptions
{
PriceData = new SessionLineItemPriceDataOptions
{
UnitAmountDecimal = result.Payment.Amount * 100,
Currency = result.Payment.Currency,
ProductData = new SessionLineItemPriceDataProductDataOptions
{
Name = _stringLocalizer.GetString("StripeProductLabel"),
},
},
Quantity = 1,
},
},
Mode = result.Payment.Mode,
SuccessUrl = $"{request.Scheme}://{request.Host}" + "/payment/complete",
CancelUrl = $"{request.Scheme}://{request.Host}" + "/payment/cancel",
Metadata = new Dictionary<string, string>()
{
{ Constants.StripeMetaDataOrderId, result.Id }
}
};
and when I receive the session objet in the completed event : session = stripeEvent.Data.Object as Stripe.Checkout.Session;
I can't get the information because the paymentIntent object is null ( information from : Retrieve Billing Address from Stripe checkout session? ).
This is an important feature from Sripe because the application is a B2B application to help professionals to create orders for their B2C business. It will avoid making custom code from something that exits in Stripe API :)
Thanks in advance
The answer you linked to is the correct way to get this information, from the payment_method on the payment_intent. I'm not sure how/why your payment_intent value would not be populated, as my testing indicates this to be initialized upon creating the session, even if I never redirect to it.
Are you certain you're creating a mode=payment session? I see that in the code you shared, but things will change a bit if you're actually doing setup or subscription mode.

Paypal .Net SDK implementation

Dear StackOverflow Community,
We have started to implement the PayPal .Net SDK in our project.
We create a Payment with the following Code:
var payment = Payment.Create(GetDefaultApiContext(), new Payment
{
intent = "sale",
payer = new Payer
{
payment_method = "paypal"
},
transactions = new List<Transaction>
{
new Transaction
{
description = "Test",
invoice_number = "009",
amount = new Amount
{
currency = "EUR",
total = "41.00",
details = new Details
{
tax = "0",
shipping = "0",
subtotal = "40",
handling_fee = "1"
}
},
item_list = new ItemList
{
items = new List<Item>
{
new Item
{
name = "Room 12",
currency = "EUR",
price = "10",
quantity = "4",
}
}
}
}
},
redirect_urls = new RedirectUrls
{
return_url = "https://google.de/",
cancel_url = "https://google.de/"
}
});
The payment is also created and a corresponding link is generated. If we now pay with our test account, the money is not debited and nothing more happens, the forwarding also works normally. However, no transaction is reported to PayPal.
It would be very nice if someone could help us with this Problem.
Thank you!
After the redirect back to the return_url you provided, you are expected to present an order review page and then when the user confirms the order you must do a payment Execute API call, which will result in the PayPal transaction. If you do not do the Execute API call, there will be no transaction.
Do not worry about money being debited from the payer account, since payer accounts have funding sources with infinite funds in sandbox.
Note also that the v1/payments SDK you are using is deprecated, you should upgrade to the current v2/checkout/orders Checkout-NET-SDK and use it to create two routes on your server, one for 'Create Transaction' and one for 'Capture Transaction', documented here.
The best approval flow to pair with your two new routes is https://developer.paypal.com/demo/checkout/#/pattern/server

Delayed Payment in Stripe Connect to unknown user

I'm writing a service which allows users to sign up for classes, at the time of sign up the class instructor may not be known, but I need to create a pending charge to assure that the instructor will be paid. Later I want to update the pending and set a finalization date, e.g update the instructors account Id and 24 hours after the class ends pay the instructor
I'm trying to understand the work flow of how to do it, but the API Doc's don't seem to be helpful.
I have a service to create Payment Intents like so:
var paymentIntentService = new PaymentIntentService();
var paymentIntentTransferDataOptions = new PaymentIntentTransferDataOptions();
var options = new PaymentIntentCreateOptions
{
Amount = paymentIntentTransactionOptions.AmountInCents,
Currency = DEFAULT_TRANSACTION_CURRENCY,
ApplicationFeeAmount = this.CalculateApplicationFee(paymentIntentTransactionOptions),
ReceiptEmail = "", // TODO Obtain this from the Logged in user
PaymentMethodTypes = new List<string> { "card" },
};
var requestOptions = new RequestOptions();
requestOptions.IdempotencyKey = INTENT_IDEM_PREFIX + paymentIntentTransactionOptions.TransactionId;
var paymentIntent = await paymentIntentService.CreateAsync(options, requestOptions);
I would like to create a pending charge with no destination at first and before completion I will update the user to send it to.
The update process I would assume to just, call Get on the PaymentIntent and Update the Sender.
My confusion lays around 3 areas.
How does the API Know what user I'm allowed to send on behalf of a user?
e.g I just provide an accountId. Does the api have full control of other accounts after registration?
How can I Create a pending charge, to assure the instructor gets paid, does that happen when I create the payment intent?
How do I finalize the transaction (Now, or preferable with a future date)
I recently created an entire sharing economy platform with Stripe Connect, so I'm fairly familiar with it.
Charging the User: You'll need to create a regular charge for the user after they schedule the class. Read this on creating charges. This will charge the user $X and hold it in the platforms account without yet transferring it.
Paying the Instructor: You'll need to create a separate transfer using source_transaction to send the money to the instructor's account. source_transaction will basically just make the transfer from the charge you made earlier from the customer. You can read more about that here.
Regarding holding the funds for 24hrs, if you use source_transaction, the money won't become available for payout until after 2 days due to processing. You can read more about that here. If you want it to become available faster, you have 2 options. Either you can enable Instant Payout (which I don't recommend) or you can have a reserve balance in your platform stripe balance to cover the one-day layover and then you can make the transfers without source_transaction.
Okay, so after asking a few more questions and reading api docs. There are a few ways of doing this. #nachshonf answer will get the job done. However if I use transfers and seperate charges, The platform will be responsible for the Stripe fees and refund.
Instead I've come up with a more complex way of doing this, but will assure less of a headache for me in the long run. Basically I create a hold via the platform, and then when the instructor is resolved will request to issue another charge for the instructor. This way all disputes go through the instructor.
First off I'll create a hold via the platform, This is pretty simple
public Task<PaymentIntent>CreatePlatformHoldAsync(long amountInCents, string customerId,
string paymentMethodId, string idem = null, string currency = DEFAULT_TRANSACTION_CURRENCY)
{
var paymentIntentService = new PaymentIntentService();
var options = new PaymentIntentCreateOptions
{
Amount = amountInCents,
Currency = currency,
//ReceiptEmail = "", // TODO Obtain this from the Logged in user
PaymentMethodTypes = new List<string> { "card" },
CustomerId = customerId,
PaymentMethodId = paymentMethodId,
CaptureMethod = "manual",
};
var requestOptions = new RequestOptions { IdempotencyKey = idem };
return paymentIntentService.CreateAsync(options, requestOptions);
}
/// <summary>
/// Confirm a payment intent, on the platform or sellerAccount
/// </summary>
/// <param name="sellerStripeAccountId">optional, omit for the platform confirm</param>
public Task<PaymentIntent> ConfirmPaymentAsync(string paymentIntentId,
string sellerStripeAccountId = null)
{
var paymentIntentService = new PaymentIntentService();
var paymentIntentConfirmOptions = new PaymentIntentConfirmOptions();
var options = new RequestOptions
{
StripeAccount = sellerStripeAccountId
};
return paymentIntentService.ConfirmAsync(paymentIntentId,
paymentIntentConfirmOptions, options);
}
First create a hold, Then confirm it to authorize the charges. Then I'll create another charge for the instructor
public Task<PaymentIntent> CreateDestinationChargeAsync(long amountInCents, long applicationFeeInCents,
string paymentMethodId, string destinationAccountId, string idem = null,
string currency = DEFAULT_TRANSACTION_CURRENCY)
{
var paymentIntentService = new PaymentIntentService();
var options = new PaymentIntentCreateOptions
{
Amount = amountInCents,
Currency = currency,
ApplicationFeeAmount = applicationFeeInCents,
//ReceiptEmail = "", // TODO Obtain this from the Logged in user
PaymentMethodTypes = new List<string> { "card" },
PaymentMethodId = paymentMethodId,
};
var requestOptions = new RequestOptions
{
IdempotencyKey = idem,
StripeAccount = destinationAccountId
};
return paymentIntentService.CreateAsync(options, requestOptions);
}
After this is paid you can cancel the platform hold or wait 7 days for the hold to age away.
Edit For Usages
public async Task<Customer> CreateCustomerAsync(string email, string sourceToken)
{
var options = new CustomerCreateOptions
{
Email = email, // "paying.user#example.com",
Source = sourceToken,
};
var service = new CustomerService();
return await service.CreateAsync(options);
}
/// <summary>
/// Creates a payment method for a customer on the sellers stripe account
/// </summary>
/// <returns></returns>
public async Task<PaymentMethod> CreatePaymentMethodAsync(string customerId, string paymentMethodId,
string stripeConnectAccountId)
{
var paymentMethodService = new PaymentMethodService();
var paymentMethodOptions = new PaymentMethodCreateOptions
{
CustomerId = customerId,
PaymentMethodId = paymentMethodId
};
var requestOptions = new RequestOptions()
{
StripeAccount = stripeConnectAccountId
};
return await paymentMethodService.CreateAsync(paymentMethodOptions, requestOptions);
}
Usage:
//set destination here
var destinationAccountId = "";
var configuration = this.GetConfiguration();
StripeConfiguration.ApiKey = configuration["Stripe:ClientSecret"];
//This is the name of the service which I define the methods above
var stripeService = new StripeConnectService(configuration);
//"tok_mastercard" is a test value to represent a paymentToken
var customer = await stripeService.CreateCustomerAsync("CustomerEmail#gmail.com", "tok_mastercard");
var sharedPaymentMethod = await stripeService.CreatePaymentMethodAsync(customer.Id,
customer.DefaultSourceId, destinationAccount.AccountId);
var paymentIntent = await stripeService.CreateDestinationChargeAsync(1000, 100,
sharedPaymentMethod.Id, destinationAccountId);
await stripeService.ConfirmPaymentAsync(paymentIntent.Id, destinationAccountId);
These are examples and are not meant for production I'm only using them to test the flow.
Note: the payment token represents a customer. I haven't implemented a way to obtain the payment token but it looks like you will need stripe.js so they can enter there card information to create a token.

How to you setup ach transfers using stripe.net

I know you have to create a customer and verify a bank account, but I'm not sure how to do the latter.
How do I go about verifying them on the customer?
To add a bank account to a customer, you need to create a token and add it to the sourceToken parameter since adding using the bank account services were not implement as of now. For Example:
BankAccountCreateOptions bankAccount = new BankAccountCreateOptions();
bankAccount.SourceBankAccount = new SourceBankAccount() {
AccountHolderName = accountHolderName,
AccountHolderType = "company",
AccountNumber = bankAccountNumber,
BankName = bankName,
Country = "US",
Currency = "usd",
RoutingNumber = bankAccountRoutingNumber
};
// Setup the customer
StripeCustomerCreateOptions options = new StripeCustomerCreateOptions();
options.Email = email;
options.Description = company;
options.PlanId = plan.Id;
options.TrialEnd = DateTime.UtcNow.AddMonths(1);
options.SourceToken = bankAccount.SourceToken;
// Create the customer
StripeCustomer customer = new StripeCustomerService().Create(options);
However I still don't know how to verify the bank account once created.
You can login to bank accounts by using test users in https://plaid.com/docs/api/#sandbox and register a user as stripe ACH. Create token by using test user bank details and create a customer for user.Do charges by using created customer id.

C# PayPal REST Api (.NET SDK)

I am using the .NET PayPal SDK (github.com/paypal/PayPal-NET-SDK) and am able to successfully create a paypal request, redirect the user, the user completes payment and all is well.
For the life of me, I can't figure out how to show the "Credit Card / No Paypal Account" screen instead of the "Login with paypal ... or click here if you don't have paypal to pay with credit card" screen.
Most of my users don't have paypal and just want to pay with a credit card (and I'd like to process it all through paypal, directly through their site so that users never give me their credit card number).
Here is the code I have that work for paying with paypal, but how can I tell it to show the "credit card/guest checkout" on paypal's landing page?
string PaypalRedirectURL(string returnURL, string cancelURL)
{
// create the transaction list
var trans = new List<Transaction>();
// make the actual transaction
var t = new Transaction()
{
description = "Payment For Something",
invoice_number = "inv1234",
amount = new Amount()
{
currency = "USD",
total = "2.00",
details = new Details
{
tax = "0",
shipping = "0",
subtotal = "2.00"
}
}
};
t.item_list = new ItemList();
t.item_list.items = new List<Item>();
Item i = new Item();
i.name = "Something I charge for";
i.quantity = "1";
i.sku = "item0001";
i.currency = "USD";
i.price = "2.00";
// Add item to item list
t.item_list.items.Add(i);
//add transaction to transaction list
trans.Add(t);
// create the payment
var payment = Payment.Create(GetAPIContext(), new Payment
{
intent = "sale",
payer = new Payer
{
payment_method = "paypal"
},
transactions = trans,
redirect_urls = new RedirectUrls
{
return_url = returnURL,
cancel_url = cancelURL
}
});
var links = payment.links.GetEnumerator();
string paypalRedirectUrl = null;
while (links.MoveNext())
{
Links lnk = links.Current;
// get the payment redirect link
if (lnk.rel.ToLower().Trim().Equals("approval_url"))
{
//saving the payapalredirect URL to which user will be redirected for payment
paypalRedirectUrl = lnk.href;
}
}
return paypalRedirectUrl;
}
I've tried numerous variations of [payment_method = "paypal"] (like (="credit_card", etc) but nothing seems to work. Again, I don't want to provide the credit card to this, I want the credit card "guest checkout" on paypal's site to show by default.
This is the screen the user gets on paypal (they can click the "pay with credit card" but I want to know if I can send them directly to the credit card screen without them having to click anything.
Current Page they are sent to:
Default paypal screen
Page I'd like the user to see by default (with the credit card fields displayed):
Desired default paypal page
Any help would be greatly appreciated .. thanks in advance

Categories

Resources