Code snippets for the YouTube video “.NET Stripe Tutorial”.
You download the full source code here.
Product.cs
namespace Shared.Models;
public class Product
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Title { get; set; } = string.Empty;
public string? Description { get; set; }
public string? ImageUrl{ get; set; }
public long Price { get; set; }
}
AppDbContext.cs
using Microsoft.EntityFrameworkCore;
using Shared.Models;
namespace Server.Data;
public sealed class AppDbContext : DbContext
{
public DbSet<Product> Products => Set<Product>();
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
/// <summary>
/// Uses Lorem Picsum for example images. Website: https://picsum.photos/.
/// </summary>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
const int amountOfProductsToSeed = 20;
var productsToSeed = new Product[amountOfProductsToSeed];
for (int i = 1; i <= amountOfProductsToSeed; i++)
{
productsToSeed[i - 1] = new Product
{
Id = Guid.NewGuid().ToString(),
Title = $"Product {i}",
Description = $"Product {i} description. This is an amazing product with a price-quality balance you won't find anywhere ele.",
ImageUrl = $"https://picsum.photos/id/{i}/500",
Price = 1000 * i,
};
}
modelBuilder.Entity<Product>().HasData(productsToSeed);
}
}
ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Server.Data;
using Shared.Models;
namespace Server.Controllers;
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
private readonly AppDbContext _appDbContext;
public ProductsController(AppDbContext appDbContext)
{
_appDbContext = appDbContext;
}
[HttpPost]
public async Task<IActionResult> Create(Product productToCreate)
{
productToCreate.Id = Guid.NewGuid().ToString();
await _appDbContext.AddAsync(productToCreate);
await _appDbContext.SaveChangesAsync();
return Ok(productToCreate);
}
[HttpGet]
public async Task<IEnumerable<Product>> Get()
{
return await _appDbContext.Products.ToListAsync();
}
[HttpPut]
public async Task<IActionResult> Update(Product updatedProduct)
{
_appDbContext.Update(updatedProduct);
await _appDbContext.SaveChangesAsync();
return Ok(updatedProduct);
}
[HttpDelete]
[Route("{productToDeleteId}")]
public async Task<IActionResult> Update(string productToDeleteId)
{
var productToDelete = await _appDbContext.Products.FindAsync(productToDeleteId);
_appDbContext.Remove(productToDelete);
await _appDbContext.SaveChangesAsync();
return NoContent();
}
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Stripe": {
"PubKey": "<YOUR KEY>",
"SecretKey": "<YOUR KEY>"
}
}
Finished program.cs
using Server.Data;
using Microsoft.EntityFrameworkCore;
using Stripe;
using System.Configuration;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
StripeConfiguration.ApiKey = builder.Configuration["Stripe:SecretKey"];
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlite("Data Source=./Data/AppDB.db"));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors();
app.UseRouting();
app.MapControllers();
app.Run();
CheckoutController.cs
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Mvc;
using Shared.Models;
using Stripe.Checkout;
namespace Server.Controllers;
[ApiController]
[Route("[controller]")]
[ApiExplorerSettings(IgnoreApi = true)]
public class CheckoutController : ControllerBase
{
private readonly IConfiguration _configuration;
private static string s_wasmClientURL = string.Empty;
public CheckoutController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpPost]
public async Task<ActionResult> CheckoutOrder([FromBody] Product product, [FromServices] IServiceProvider sp)
{
var referer = Request.Headers.Referer;
s_wasmClientURL = referer[0];
// Build the URL to which the customer will be redirected after paying.
var server = sp.GetRequiredService<IServer>();
var serverAddressesFeature = server.Features.Get<IServerAddressesFeature>();
string? thisApiUrl = null;
if (serverAddressesFeature is not null)
{
thisApiUrl = serverAddressesFeature.Addresses.FirstOrDefault();
}
if (thisApiUrl is not null)
{
var sessionId = await CheckOut(product, thisApiUrl);
var pubKey = _configuration["Stripe:PubKey"];
var checkoutOrderResponse = new CheckoutOrderResponse()
{
SessionId = sessionId,
PubKey = pubKey
};
return Ok(checkoutOrderResponse);
}
else
{
return StatusCode(500);
}
}
[NonAction]
public async Task<string> CheckOut(Product product, string thisApiUrl)
{
// Create a payment flow from the items in the cart.
// Gets sent to Stripe API.
var options = new SessionCreateOptions
{
// Stripe calls the URLs below when certain checkout events happen such as success and failure.
SuccessUrl = $"{thisApiUrl}/checkout/success?sessionId=" + "{CHECKOUT_SESSION_ID}", // Customer paid.
CancelUrl = s_wasmClientURL + "failed", // Checkout cancelled.
PaymentMethodTypes = new List<string> // Only card available in test mode?
{
"card"
},
LineItems = new List<SessionLineItemOptions>
{
new()
{
PriceData = new SessionLineItemPriceDataOptions
{
UnitAmount = product.Price, // Price is in USD cents.
Currency = "USD",
ProductData = new SessionLineItemPriceDataProductDataOptions
{
Name = product.Title,
Description = product.Description,
Images = new List<string> { product.ImageUrl }
},
},
Quantity = 1,
},
},
Mode = "payment" // One-time payment. Stripe supports recurring 'subscription' payments.
};
var service = new SessionService();
var session = await service.CreateAsync(options);
return session.Id;
}
[HttpGet("success")]
// Automatic query parameter handling from ASP.NET.
// Example URL: https://localhost:7051/checkout/success?sessionId=si_123123123123
public ActionResult CheckoutSuccess(string sessionId)
{
var sessionService = new SessionService();
var session = sessionService.Get(sessionId);
// Here you can save order and customer details to your database.
var total = session.AmountTotal.Value;
var customerEmail = session.CustomerDetails.Email;
return Redirect(s_wasmClientURL + "success");
}
}
checkout.js
function checkout(pubKey, sessionId) {
const stripe = Stripe(pubKey);
stripe.redirectToCheckout({ sessionId });
}
JavaScript imports
<script src="https://js.stripe.com/v3/"></script>
<script src="js/checkout.js"></script>
Index.razor.cs
using Microsoft.AspNetCore.Components;
using System.Net.Http.Json;
using Shared.Models;
using Microsoft.JSInterop;
using Newtonsoft.Json;
namespace Client.Pages;
public partial class Index
{
[Inject]
public HttpClient HttpClient { get; set; } = default!;
[Inject]
public IJSRuntime JsRuntime { get; set; } = default!;
private List<Product>? _products;
private IEnumerable<Product[]>? _productChunksOf4;
private const string DevApiBaseAddress = "https://localhost:7161";
protected override async Task OnInitializedAsync()
{
_products = await HttpClient.GetFromJsonAsync<List<Product>>($"{DevApiBaseAddress}/products");
if (_products is not null)
{
_productChunksOf4 = _products.Chunk(4);
}
}
private async Task OnClickBtnBuyNowAsync(Product product)
{
var response = await HttpClient.PostAsJsonAsync($"{DevApiBaseAddress}/checkout", product);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
var checkoutOrderResponse = JsonConvert.DeserializeObject<CheckoutOrderResponse>(responseBody);
// Opens up Stripe.
await JsRuntime.InvokeVoidAsync("checkout", checkoutOrderResponse.PubKey, checkoutOrderResponse.SessionId);
}
}
Index.razor
@page "/"
<PageTitle>Store</PageTitle>
<h1>Store</h1>
<hr class="mb-5" />
@if (_productChunksOf4 is not null)
{
foreach (var chunkedProducts in _productChunksOf4)
{
<div class="row mb-5">
@foreach (var product in chunkedProducts)
{
<div class="col-sm-3 mb-5 mb-sm-0">
<div class="card" style="width: 18rem;">
<img src="@product.ImageUrl" class="card-img-top">
<div class="card-body">
<h5 class="card-title fw-bold">@product.Title</h5>
<p class="card-text">@product.Description</p>
<h5 class="mb-4"><strong>$@(product.Price / 100)</strong></h5>
<button @onclick="() => OnClickBtnBuyNowAsync(product)" class="btn btn-dark btn-lg d-block">BUY NOW</button>
</div>
</div>
</div>
}
</div>
}
}
else
{
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
}