# Marketplace quickstart
# Build a marketplace integration
> You can customize this guide by selecting integration options in the [Interactive platform guide](https://docs.stripe.com/connect/interactive-platform-guide.md).
Use this working code sample to integrate Stripe Hosted Checkout for one-time payments with Stripe Connect. With this approach, Stripe hosts the checkout page for you.
const handleCreateProduct = async (formData) => {
if (!accountId) return;
if (needsOnboarding) return;
const response = await fetch("/api/create-product", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...formData, accountId }),
});
const data = await response.json();
setShowForm(false);
};
export default function Page() {
// Get the checkout session ID from the URL
const [searchParams] = useSearchParams();
const sessionId = searchParams.get("session_id");
const [accountId] = useState(localStorage.getItem("accountId"));
return (
);
}
const handleCreateAccount = async (e) => {
e.preventDefault();
try {
const response = await fetch("/api/create-connect-account", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});
if (!response.ok) {
throw new Error("Failed to create account");
}
const data = await response.json();
// Update the account ID in the provider
setAccountId(data.accountId);
} catch (error) {
console.error("Error creating account:", error);
}
};
const handleStartOnboarding = async () => {
try {
const response = await fetch("/api/create-account-link", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ accountId }),
});
if (!response.ok) {
throw new Error("Failed to create account link");
}
const data = await response.json();
window.location.href = data.url;
} catch (error) {
console.error("Error creating account link:", error);
}
};
const Product = ({ name, price, priceId, period, image }) => {
const { accountId } = useAccount();
return (
{name}
{price} {period && `/ ${period}`}
);
};
const fetchProducts = async () => {
if (!accountId) return;
if (needsOnboarding) return;
const response = await fetch(`/api/products/${accountId}`);
const data = await response.json();
setProducts(data);
};
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// Create a sample product and return a price for it
router.post("/create-product", async (req, res) => {
const productName = req.body.productName;
const productDescription = req.body.productDescription;
const productPrice = req.body.productPrice;
const accountId = req.body.accountId; // Get the connected account ID
try {
// Create the product on the platform
const product = await stripe.products.create(
{
name: productName,
description: productDescription,
metadata: { stripeAccount: accountId }
}
);
// Create a price for the product on the platform
const price = await stripe.prices.create(
{
product: product.id,
unit_amount: productPrice,
currency: "usd",
metadata: { stripeAccount: accountId }
},
);
res.json({
productName: productName,
productDescription: productDescription,
productPrice: productPrice,
priceId: price.id,
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Create a Connected Account
router.post("/create-connect-account", async (req, res) => {
try {
// Create a Connect account with the specified controller properties
const account = await stripe.v2.core.accounts.create({
display_name: req.body.email,
contact_email: req.body.email,
dashboard: "express",
defaults: {
responsibilities: {
fees_collector: "application",
losses_collector: "application",
},
},
identity: {
country: "US",
entity_type: "company",
},
configuration: {
recipient: {
capabilities: {
stripe_balance: {
stripe_transfers: {
requested: true,
},
},
},
},
},
});
res.json({ accountId: account.id });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Create Account Link for onboarding
router.post("/create-account-link", async (req, res) => {
const accountId = req.body.accountId;
try {
const accountLink = await stripe.v2.core.accountLinks.create({
account: accountId,
use_case: {
type: 'account_onboarding',
account_onboarding: {
configurations: ['recipient'],
refresh_url: 'https://example.com',
return_url: `https://example.com?accountId=${accountId}`,
},
},
});
res.json({ url: accountLink.url });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Fetch products for a specific account
router.get("/products/:accountId", async (req, res) => {
const { accountId } = req.params;
try {
const prices = await stripe.prices.search({
query: `metadata['stripeAccount']:'${accountId}' AND active:'true'`,
expand: ["data.product"],
limit: 100,
});
res.json(
prices.data.map((price) => ({
id: price.product.id,
name: price.product.name,
description: price.product.description,
price: price.unit_amount,
priceId: price.id,
period: price.recurring ? price.recurring.interval : null,
image: "https://i.imgur.com/6Mvijcm.png",
}))
);
} catch (err) {
console.error("Error fetching prices:", err);
res.status(500).json({ error: err.message });
}
});
// Create checkout session
router.post("/create-checkout-session", async (req, res) => {
const { priceId, accountId } = req.body;
// Get the price's type from Stripe
const price = await stripe.prices.retrieve(priceId);
const priceType = price.type;
const mode = priceType === 'recurring' ? 'subscription' : 'payment';
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: priceId,
quantity: 1,
},
],
mode: mode,
// Defines where Stripe will redirect a customer after successful payment
success_url: `${process.env.DOMAIN}/done?session_id={CHECKOUT_SESSION_ID}`,
// Defines where Stripe will redirect if a customer cancels payment
cancel_url: `${process.env.DOMAIN}`,
...(mode === 'subscription' ? {
subscription_data: {
application_fee_amount: 123,
transfer_data: {
destination: accountId,
},
},
} : {
payment_intent_data: {
application_fee_amount: 123,
transfer_data: {
destination: accountId,
},
},
}),
});
// Redirect to the Stripe hosted checkout URL
res.redirect(303, session.url);
});
router.post(
"/webhook",
express.raw({ type: "application/json" }),
(request, response) => {
let event = request.body;
// Replace this endpoint secret with your endpoint's unique secret
// If you are testing with the CLI, find the secret by running 'stripe listen'
// If you are using an endpoint defined with the API or dashboard, look in your webhook settings
// at https://dashboard.stripe.com/webhooks
const endpointSecret = "";
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with JSON.parse
if (endpointSecret) {
const signature = request.headers["stripe-signature"];
try {
event = stripe.webhooks.constructEvent(
request.body,
signature,
endpointSecret
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
return response.sendStatus(400);
}
}
let stripeObject;
let status;
// Handle the event
switch (event.type) {
case "checkout.session.completed":
stripeObject = event.data.object;
status = stripeObject.status;
console.log(`Checkout Session status is ${status}.`);
// Then define and call a method to handle the subscription deleted.
// handleCheckoutSessionCompleted(stripeObject);
break;
case "checkout.session.async_payment_failed":
stripeObject = event.data.object;
status = stripeObject.status;
console.log(`Checkout Session status is ${status}.`);
// Then define and call a method to handle the subscription deleted.
// handleCheckoutSessionFailed(stripeObject);
break;
default:
// Unexpected event type
console.log(`Unhandled event type ${event.type}.`);
}
// Return a 200 response to acknowledge receipt of the event
response.send();
}
);
Stripe.api_key = ENV["STRIPE_SECRET_KEY"]
stripe_client = Stripe::StripeClient.new(ENV["STRIPE_SECRET_KEY"])
\# Create a sample product and return a price for it
post "/api/create-product" do
data = parse_request_body
product_name = data['productName']
product_description = data['productDescription']
product_price = data['productPrice']
account_id = data['accountId']
begin
# Create the product on the platform
product = Stripe::Product.create({
name: product_name,
description: product_description,
metadata: { stripe_account: account_id }
})
# Create a price for the product on the platform
price = Stripe::Price.create({
product: product.id,
unit_amount: product_price,
currency: 'usd',
metadata: { stripe_account: account_id }
})
content_type :json
{
productName: product_name,
productDescription: product_description,
productPrice: product_price,
priceId: price.id
}.to_json
rescue Stripe::StripeError => e
status 500
{ error: e.message }.to_json
end
end
\# Create a Connected Account
post "/api/create-connect-account" do
data = parse_request_body
begin
account = stripe_client.v2.core.accounts.create({
display_name: data['email'],
contact_email: data['email'],
dashboard: 'express',
defaults: {
responsibilities: {
fees_collector: 'application',
losses_collector: 'application',
},
},
identity: {
country: 'US',
entity_type: 'company',
},
configuration: {
recipient: {
capabilities: {
stripe_balance: {
stripe_transfers: { requested: true },
},
},
},
},
})
content_type :json
{ accountId: account.id }.to_json
rescue Stripe::StripeError => e
status 500
{ error: e.message }.to_json
end
end
\# Create Account Link for onboarding
post "/api/create-account-link" do
data = parse_request_body
account_id = data['accountId']
begin
account_link = stripe_client.v2.core.account_links.create({
account: account_id,
use_case: {
type: 'account_onboarding',
account_onboarding: {
configurations: ['recipient'],
refresh_url: 'https://example.com',
return_url: "https://example.com?accountId=#{account_id}",
},
},
})
content_type :json
{ url: account_link.url }.to_json
rescue Stripe::StripeError => e
status 500
{ error: e.message }.to_json
end
end
\# Fetch products for a specific account
get "/api/products/:account_id" do
account_id = params[:account_id]
begin
prices = Stripe::Price.search({
query: "metadata['stripeAccount']:'#{account_id}' AND active:'true'",
expand: ['data.product'],
limit: 100,
})
products = prices.data.map do |price|
{
id: price.product.id,
name: price.product.name,
description: price.product.description,
price: price.unit_amount,
priceId: price.id,
image: 'https://i.imgur.com/6Mvijcm.png'
}
end
content_type :json
products.to_json
rescue Stripe::StripeError => e
status 500
{ error: e.message }.to_json
end
end
session_params = {
line_items: [
{
price: price_id,
quantity: 1,
},
],
mode: mode,
\# Defines where Stripe will redirect a customer after successful payment
success_url: "#{ENV['DOMAIN']}/done?session_id={CHECKOUT_SESSION_ID}",
# Defines where Stripe will redirect if a customer cancels payment
cancel_url: "#{ENV['DOMAIN']}",
}
# Add Connect-specific parameters based on payment mode
if mode == 'subscription'
session_params[:subscription_data] ||= {}
session_params[:subscription_data][:application_fee_amount] = 123
session_params[:subscription_data][:transfer_data] = {
destination: account_id,
}
else
session_params[:payment_intent_data] = {
application_fee_amount: 123,
transfer_data: {
destination: account_id,
},
}
end
session = Stripe::Checkout::Session.create(session_params)
post "/api/webhook" do
request.body.rewind
payload = request.body.read
\# Replace this endpoint secret with your endpoint's unique secret
# If you are testing with the CLI, find the secret by running 'stripe listen'
# If you are using an endpoint defined with the API or dashboard, look in your webhook settings
# at https://dashboard.stripe.com/webhooks
endpoint_secret = ""
# Only verify the event if you have an endpoint secret defined.
# Otherwise use the basic event deserialized directly.
if endpoint_secret != ""
signature = request.env["HTTP_STRIPE_SIGNATURE"]
begin
event = Stripe::Webhook.construct_event(payload, signature, endpoint_secret)
rescue => e
puts "Webhook signature verification failed. #{e.message}"
halt 400
end
else
event = JSON.parse(payload, symbolize_names: true)
end
case event[:type]
when "checkout.session.completed"
stripe_object = event[:data][:object]
status = stripe_object[:status]
puts "Checkout Session status is #{status}."
# handle_checkout_session_completed(stripe_object)
when "checkout.session.async_payment_failed"
stripe_object = event[:data][:object]
status = stripe_object[:status]
puts "Checkout Session status is #{status}."
# handle_checkout_session_failed(stripe_object)
else
# Unexpected event type
puts "Unhandled event type #{event[:type]}."
end
status 200
end
post "/api/thin-webhook" do
request.body.rewind
payload = request.body.read
# Replace this endpoint secret with your endpoint's unique secret
# If you are testing with the CLI, find the secret by running 'stripe listen'
# If you are using an endpoint defined with the API or dashboard, look in your webhook settings
# at https://dashboard.stripe.com/webhooks
thin_endpoint_secret = nil
signature = request.env["HTTP_STRIPE_SIGNATURE"]
begin
event_notif = stripe_client.parse_event_notification(payload, signature, thin_endpoint_secret)
rescue => e
puts "Webhook signature verification failed. #{e.message}"
halt 400
end
if event_notif.type == "v2.account.created"
stripe_object = event_notif.fetch_related_object
event = event_notif.fetch_event
# handle_v2_account_created(stripe_object)
else
puts "Unhandled event type #{event_notif.type}."
end
status 200
end
import stripe
from stripe import StripeClient
stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
@app.route('/api/create-product', methods=['POST'])
def create_product():
data = parse_request_body()
product_name = data['productName']
product_description = data['productDescription']
product_price = data['productPrice']
account_id = data['accountId']
try:
\# Create the product on the platform
product = stripe.Product.create(
name=product_name,
description=product_description,
metadata={'stripeAccount': account_id}
)
# Create a price for the product on the platform
price = stripe.Price.create(
product=product.id,
unit_amount=product_price,
currency='usd',
metadata={'stripeAccount': account_id}
)
return jsonify({
'productName': product_name,
'productDescription': product_description,
'productPrice': product_price,
'priceId': price.id
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/create-connect-account', methods=['POST'])
def create_connect_account():
data = parse_request_body()
try:
account = stripe_client.v2.core.accounts.create({
"display_name": data.get("email"),
"contact_email": data.get("email"),
"dashboard": "express",
"defaults": {
"responsibilities": {
"fees_collector": "application",
"losses_collector": "application",
}
},
"identity": {
"country": "US",
"entity_type": "company",
},
"configuration": {
"recipient": {
"capabilities": {
"stripe_balance": {
"stripe_transfers": {"requested": True},
}
}
},
},
})
return jsonify({'accountId': account.id})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/create-account-link', methods=['POST'])
def create_account_link():
data = parse_request_body()
account_id = data['accountId']
try:
account_link = stripe_client.v2.core.account_links.create({
"account": account_id,
"use_case": {
"type": "account_onboarding",
"account_onboarding": {
"configurations": ["recipient"],
"refresh_url": "https://example.com",
"return_url": f"https://example.com?accountId={account_id}",
},
},
})
return jsonify({'url': account_link.url})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/products/', methods=['GET'])
def get_products(account_id):
try:
prices = stripe.Price.search(
query=f"metadata['stripeAccount']:'{account_id}' AND active:'true'",
expand=['data.product'],
limit=100
)
products = []
for price in prices.data:
products.append({
'id': price.product.id,
'name': price.product.name,
'description': price.product.description,
'price': price.unit_amount,
'priceId': price.id,
'image': 'https://i.imgur.com/6Mvijcm.png'
})
return jsonify(products)
except Exception as e:
return jsonify({'error': str(e)}), 500
checkout_session = stripe.checkout.Session.create(
line_items=[
{
'price': price_id,
'quantity': 1
}
],
mode=mode,
\# Defines where Stripe will redirect a customer after successful payment
success_url=f"{os.getenv('DOMAIN')}/done?session_id={{CHECKOUT_SESSION_ID}}",
# Defines where Stripe will redirect if a customer cancels payment
cancel_url=f"{os.getenv('DOMAIN')}",
**(
{
'subscription_data': {
'application_fee_amount': 123,
'transfer_data': {
'destination': account_id,
},
}
} if mode == 'subscription' else {
'payment_intent_data': {
'application_fee_amount': 123,
'transfer_data': {
'destination': account_id,
},
}
}
)
)
@app.route('/api/webhook', methods=['POST'])
def webhook_received():
\# Replace this endpoint secret with your endpoint's unique secret
# If you are testing with the CLI, find the secret by running 'stripe listen'
# If you are using an endpoint defined with the API or dashboard, look in your webhook settings
# at https://dashboard.stripe.com/webhooks
endpoint_secret = ''
request_data = json.loads(request.data)
# Only verify the event if you have an endpoint secret defined.
# Otherwise use the basic event deserialized with JSON.parse
if endpoint_secret:
sig_header = request.headers.get('stripe-signature')
try:
event = stripe.Webhook.construct_event(
request.data, sig_header, endpoint_secret
)
except stripe.error.SignatureVerificationError as e:
app.logger.info('Webhook signature verification failed.')
return jsonify({'error': str(e)}), 400
else:
event = request_data
# Handle the event
match event['type']:
case 'checkout.session.completed':
session = event['data']['object']
status = session['status']
app.logger.info(f'Checkout Session status is {status}.')
# Then define and call a method to handle the checkout session completed.
# handle_checkout_session_completed(session);
case 'checkout.session.async_payment_failed':
session = event['data']['object']
status = session['status']
app.logger.info(f'Checkout Session status is {status}.')
# Then define and call a method to handle the checkout session failed.
# handle_checkout_session_failed(session);
case _:
# Unexpected event type
app.logger.info(f'Unhandled event type {event["type"]}')
# Return a 200 response to acknowledge receipt of the event
return jsonify({'status': 'success'})
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
http.HandleFunc("/api/webhook", handleWebhook)
http.HandleFunc("/api/thin-webhook", handleThinWebhook)
// Create a sample product and return a price for it
func createProduct(w http.ResponseWriter, r *http.Request) {
data := parseRequestBody(r)
productName := data["productName"].(string)
productDescription := data["productDescription"].(string)
productPrice := int64(data["productPrice"].(float64))
accountId := data["accountId"].(string)
var p *stripe.Product
var pr *stripe.Price
var err error
// Create the product on the platform
productParams := &stripe.ProductParams{
Name: stripe.String(productName),
Description: stripe.String(productDescription),
}
productParams.AddMetadata("stripeAccount", accountId)
p, err = product.New(productParams)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Create a price for the product on the platform
priceParams := &stripe.PriceParams{
Product: stripe.String(p.ID),
UnitAmount: stripe.Int64(productPrice),
Currency: stripe.String("usd"),
}
priceParams.AddMetadata("stripeAccount", accountId)
pr, err = price.New(priceParams)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Return the product and price information
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"productName": productName,
"productDescription": productDescription,
"productPrice": productPrice,
"priceId": pr.ID,
})
}
// Create a Connected Account
func createConnectAccount(w http.ResponseWriter, r *http.Request) {
data := parseRequestBody(r)
email := data["email"].(string)
sc := stripe.NewClient(os.Getenv("STRIPE_SECRET_KEY"))
displayName := email
if displayName == "" {
displayName = "Sample Store"
}
params := &stripe.V2CoreAccountCreateParams{
ContactEmail: stripe.String(email),
DisplayName: stripe.String(displayName),
Identity: &stripe.V2CoreAccountCreateIdentityParams{
Country: stripe.String("US"),
EntityType: stripe.String("company"),
},
Configuration: &stripe.V2CoreAccountCreateConfigurationParams{
Recipient: &stripe.V2CoreAccountCreateConfigurationRecipientParams{
Capabilities: &stripe.V2CoreAccountCreateConfigurationRecipientCapabilitiesParams{
StripeBalance: &stripe.V2CoreAccountCreateConfigurationRecipientCapabilitiesStripeBalanceParams{
StripeTransfers: &stripe.V2CoreAccountCreateConfigurationRecipientCapabilitiesStripeBalanceStripeTransfersParams{
Requested: stripe.Bool(true),
},
},
},
},
},
Defaults: &stripe.V2CoreAccountCreateDefaultsParams{
Responsibilities: &stripe.V2CoreAccountCreateDefaultsResponsibilitiesParams{
FeesCollector: stripe.String("application"),
LossesCollector: stripe.String("application"),
},
},
Dashboard: stripe.String("express"),
}
result, err := sc.V2CoreAccounts.Create(context.TODO(), params)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"accountId": result.ID})
}
// Create Account Link for onboarding
func createAccountLink(w http.ResponseWriter, r *http.Request) {
data := parseRequestBody(r)
accountId := data["accountId"].(string)
sc := stripe.NewClient(os.Getenv("STRIPE_SECRET_KEY"))
params := &stripe.V2CoreAccountLinkCreateParams{
Account: stripe.String(accountId),
UseCase: &stripe.V2CoreAccountLinkCreateUseCaseParams{
Type: stripe.String("account_onboarding"),
AccountOnboarding: &stripe.V2CoreAccountLinkCreateUseCaseAccountOnboardingParams{
Configurations: []*string{
stripe.String("recipient"),
},
RefreshURL: stripe.String("https://example.com"),
ReturnURL: stripe.String("https://example.com?accountId=" + accountId),
},
},
}
link, err := sc.V2CoreAccountLinks.Create(context.TODO(), params)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"url": link.URL})
}
// Fetch products for a specific account
func getProducts(w http.ResponseWriter, r *http.Request) {
accountId := r.URL.Path[len("/api/products/"):]
// Search for prices with metadata matching the account ID
params := &stripe.PriceSearchParams{}
params.Query = fmt.Sprintf("metadata['stripeAccount']:'%s' AND active:'true'", accountId)
params.Limit = stripe.Int64(100)
params.AddExpand("data.product")
var priceList = price.Search(params).PriceSearchResult()
var products []map[string]interface{}
for _, p := range priceList.Data {
products = append(products, map[string]interface{}{
"id": p.Product.ID,
"name": p.Product.Name,
"description": p.Product.Description,
"price": p.UnitAmount,
"priceId": p.ID,
"image": "https://i.imgur.com/6Mvijcm.png",
})
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(products)
}
// Create checkout session
func createCheckoutSession(w http.ResponseWriter, r *http.Request) {
data := parseRequestBody(r)
accountId := data["accountId"].(string)
priceId := data["priceId"].(string)
// Get the price's type from Stripe
p, _ := price.Get(priceId, nil)
priceType := p.Type
var mode string
if priceType == "recurring" {
mode = "subscription"
} else {
mode = "payment"
}
// Build the basic checkout session params
checkoutSessionParams := &stripe.CheckoutSessionParams{
LineItems: []*stripe.CheckoutSessionLineItemParams{
{
Price: stripe.String(priceId),
Quantity: stripe.Int64(1),
},
},
Mode: stripe.String(mode),
// Defines where Stripe will redirect a customer after successful payment
SuccessURL: stripe.String(os.Getenv("DOMAIN") + "/done?session_id={CHECKOUT_SESSION_ID}"),
// Defines where Stripe will redirect if a customer cancels payment
CancelURL: stripe.String(os.Getenv("DOMAIN")),
}
// Add Connect-specific parameters based on payment mode
if mode == "subscription" {
if checkoutSessionParams.SubscriptionData == nil {
checkoutSessionParams.SubscriptionData = &stripe.CheckoutSessionSubscriptionDataParams{}
}
checkoutSessionParams.SubscriptionData.ApplicationFeeAmount = stripe.Int64(123)
checkoutSessionParams.SubscriptionData.TransferData = &stripe.CheckoutSessionSubscriptionDataTransferDataParams{
Destination: stripe.String(accountId),
}
} else {
checkoutSessionParams.PaymentIntentData = &stripe.CheckoutSessionPaymentIntentDataParams{
ApplicationFeeAmount: stripe.Int64(123),
TransferData: &stripe.CheckoutSessionPaymentIntentDataTransferDataParams{
Destination: stripe.String(accountId),
},
}
}
s, err := checkoutSession.New(checkoutSessionParams)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Redirect to the Stripe hosted checkout URL
http.Redirect(w, r, s.URL, http.StatusSeeOther)
}
// Stripe webhook endpoint
func handleWebhook(w http.ResponseWriter, r *http.Request) {
payload, _ := io.ReadAll(r.Body)
// Replace this endpoint secret with your endpoint's unique secret
// If you are testing with the CLI, find the secret by running 'stripe listen'
// If you are using an endpoint defined with the API or dashboard, look in your webhook settings
// at https://dashboard.stripe.com/webhooks
endpointSecret := ""
var event stripe.Event
if endpointSecret != "" {
signature := r.Header.Get("Stripe-Signature")
e, err := webhook.ConstructEvent(payload, signature, endpointSecret)
if err != nil {
return
}
event = e
} else {
json.Unmarshal(payload, &event)
}
// Handle the event
switch event.Type {
case "checkout.session.completed":
var session stripe.CheckoutSession
json.Unmarshal(event.Data.Raw, &session)
status := session.Status
log.Printf("Checkout Session status is %s.\n", status)
// Then define and call a method to handle the checkout session completed.
// handleCheckoutSessionCompleted(session);
case "checkout.session.async_payment_failed":
var session stripe.CheckoutSession
json.Unmarshal(event.Data.Raw, &session)
status := session.Status
log.Printf("Checkout Session status is %s.\n", status)
// Then define and call a method to handle the checkout session failed.
// handleCheckoutSessionFailed(session);
default:
// Unexpected event type
log.Printf("Unhandled event type %s.\n", event.Type)
}
// Return a 200 response to acknowledge receipt of the event
w.WriteHeader(http.StatusOK)
}
$stripe = new \Stripe\StripeClient([
"api_key" => $_ENV['STRIPE_SECRET_KEY'],
]);
// Create a sample product and return a price for it
if ($_SERVER['REQUEST_URI'] === '/api/create-product' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$data = parseRequestBody();
$productName = $data['productName'];
$productDescription = $data['productDescription'];
$productPrice = $data['productPrice'];
$accountId = $data['accountId'];
try {
// Create the product on the platform
$product = $stripe->products->create([
'name' => $productName,
'description' => $productDescription,
'metadata' => ['stripeAccount' => $accountId]
]);
// Create a price for the product on the platform
$price = $stripe->prices->create([
'product' => $product->id,
'unit_amount' => $productPrice,
'currency' => 'usd',
'metadata' => ['stripeAccount' => $accountId]
]);
echo json_encode([
'productName' => $productName,
'productDescription' => $productDescription,
'productPrice' => $productPrice,
'priceId' => $price->id,
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}
// Create a Connected Account
if ($_SERVER['REQUEST_URI'] === '/api/create-connect-account' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$data = parseRequestBody();
try {
$account = $stripe->v2->core->accounts->create([
'display_name' => $data['email'],
'contact_email' => $data['email'],
'dashboard' => 'express',
'defaults' => [
'responsibilities' => [
'fees_collector' => 'application',
'losses_collector' => 'application',
],
],
'identity' => [
'country' => 'US',
'entity_type' => 'company',
],
'configuration' => [
'recipient' => [
'capabilities' => [
'stripe_balance' => [
'stripe_transfers' => ['requested' => true],
],
],
],
],
]);
echo json_encode(['accountId' => $account->id]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}
// Create Account Link for onboarding
if ($_SERVER['REQUEST_URI'] === '/api/create-account-link' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$data = parseRequestBody();
$accountId = $data['accountId'];
try {
$accountLink = $stripe->v2->core->accountLinks->create([
'account' => $accountId,
'use_case' => [
'type' => 'account_onboarding',
'account_onboarding' => [
'configurations' => ['recipient'],
'refresh_url' => 'https://example.com',
'return_url' => 'https://example.com?accountId=' . $accountId,
],
],
]);
echo json_encode(['url' => $accountLink->url]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}
// Fetch products for a specific account
if (preg_match('/^\/api\/products\/(.+)$/', $_SERVER['REQUEST_URI'], $matches) && $_SERVER['REQUEST_METHOD'] === 'GET') {
$accountId = $matches[1];
try {
$prices = $stripe->prices->search([
'query' => "metadata['stripeAccount']:'" . $accountId . "' AND active:'true'",
'expand' => ['data.product'],
'limit' => 100,
]);
$products = [];
foreach ($prices->data as $price) {
$products[] = [
'id' => $price->product->id,
'name' => $price->product->name,
'description' => $price->product->description,
'price' => $price->unit_amount,
'priceId' => $price->id,
'image' => 'https://i.imgur.com/6Mvijcm.png',
];
}
echo json_encode($products);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}
$checkout_params = [
'line_items' => [[
'price' => $priceId,
'quantity' => 1,
]],
'mode' => $mode,
// Defines where Stripe will redirect a customer after successful payment
'success_url' => $_ENV['DOMAIN'] . '/done?session_id={CHECKOUT_SESSION_ID}',
// Defines where Stripe will redirect if a customer cancels payment
'cancel_url' => $_ENV['DOMAIN'],
];
// Add Connect-specific parameters based on payment mode
if ($mode === 'subscription') {
$checkout_params['subscription_data'] = array_merge(
$checkout_params['subscription_data'] ?? [],
[
'application_fee_amount' => 123,
'transfer_data' => [
'destination' => $accountId,
],
]
);
} else {
$checkout_params['payment_intent_data'] = [
'application_fee_amount' => 123,
'transfer_data' => [
'destination' => $accountId,
],
];
}
$checkout_session = $stripe->checkout->sessions->create($checkout_params);
if ($_SERVER['REQUEST_URI'] === '/api/webhook' && $_SERVER['REQUEST_METHOD'] === 'POST') {
$payload = @file_get_contents('php://input');
$event = null;
// Replace this endpoint secret with your endpoint's unique secret
// If you are testing with the CLI, find the secret by running 'stripe listen'
// If you are using an endpoint defined with the API or dashboard, look in your webhook settings
// at https://dashboard.stripe.com/webhooks
$endpoint_secret = '';
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with json_decode
if ($endpoint_secret) {
try {
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = $stripe->webhooks->constructEvent(
$payload, $sig_header, $endpoint_secret
);
} catch(\UnexpectedValueException $e) {
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
http_response_code(400);
exit();
}
} else {
$event = json_decode($payload);
}
$stripeObject = null;
$status = null;
// Handle the event
switch ($event->type) {
case 'checkout.session.completed':
$stripeObject = $event->data->object;
$status = $stripeObject->status;
error_log("Checkout Session status is " . $status);
// Then define and call a method to handle the checkout session completed.
// handleCheckoutSessionCompleted($stripeObject);
break;
case 'checkout.session.async_payment_failed':
$stripeObject = $event->data->object;
$status = $stripeObject->status;
error_log("Checkout Session status is " . $status);
// Then define and call a method to handle the checkout session failed.
// handleCheckoutSessionFailed($stripeObject);
break;
default:
error_log('Unhandled event type ' . $event->type);
}
// Return a 200 response to acknowledge receipt of the event
http_response_code(200);
exit();
}
Stripe.apiKey = dotenv.get("STRIPE_SECRET_KEY");
// v2 API client
StripeClient v2Client = new StripeClient(dotenv.get("STRIPE_SECRET_KEY"));
// Create a sample product and return a price for it
post("/api/create-product", (request, response) -> {
String productName = parseRequestBody(request, "productName");
String productDescription = parseRequestBody(request, "productDescription");
Long productPrice = Long.parseLong(parseRequestBody(request, "productPrice"));
String accountId = parseRequestBody(request, "accountId");
try {
Product product;
Price price;
// Set the request with metadata for the system's reference
Map metadata = new HashMap<>();
metadata.put("stripeAccount", accountId);
// Create the product on the platform
ProductCreateParams productParams = ProductCreateParams.builder()
.setName(productName)
.setDescription(productDescription)
.putAllMetadata(metadata)
.build();
product = Product.create(productParams);
// Create a price for the product on the platform
PriceCreateParams priceParams = PriceCreateParams.builder()
.setProduct(product.getId())
.setUnitAmount(productPrice)
.setCurrency("usd")
.putAllMetadata(metadata)
.build();
price = Price.create(priceParams);
return new Gson().toJson(Map.of(
"productName", productName,
"productDescription", productDescription,
"productPrice", productPrice,
"priceId", price.getId()
));
} catch (StripeException e) {
response.status(500);
return new Gson().toJson(Map.of("error", e.getMessage()));
}
});
// Create a Connected Account
post("/api/create-connect-account", (request, response) -> {
String email = parseRequestBody(request, "email");
try {
// Create v2 Account for marketplace
AccountCreateParams params =
AccountCreateParams.builder()
.setContactEmail(email)
.setDisplayName(email)
.setIdentity(
AccountCreateParams.Identity.builder()
.setCountry(AccountCreateParams.Identity.Country.US)
.setEntityType(AccountCreateParams.Identity.EntityType.COMPANY)
.build()
)
.setConfiguration(
AccountCreateParams.Configuration.builder()
.setRecipient(
AccountCreateParams.Configuration.Recipient.builder()
.setCapabilities(
AccountCreateParams.Configuration.Recipient.Capabilities.builder()
.setStripeBalance(
AccountCreateParams.Configuration.Recipient.Capabilities.StripeBalance.builder()
.setStripeTransfers(
AccountCreateParams.Configuration.Recipient.Capabilities.StripeBalance.StripeTransfers.builder()
.setRequested(true)
.build()
)
.build()
)
.build()
)
.build()
)
.build()
)
.setDefaults(
AccountCreateParams.Defaults.builder()
.setResponsibilities(
AccountCreateParams.Defaults.Responsibilities.builder()
.setFeesCollector(AccountCreateParams.Defaults.Responsibilities.FeesCollector.APPLICATION)
.setLossesCollector(AccountCreateParams.Defaults.Responsibilities.LossesCollector.APPLICATION)
.build()
)
.build()
)
.setDashboard(AccountCreateParams.Dashboard.EXPRESS)
.build();
Account account = v2Client.v2().core().accounts().create(params);
return new Gson().toJson(Map.of("accountId", account.getId()));
} catch (StripeException e) {
response.status(500);
return new Gson().toJson(Map.of("error", e.getMessage()));
}
});
// Create Account Link for onboarding
post("/api/create-account-link", (request, response) -> {
String accountId = parseRequestBody(request, "accountId");
try {
com.stripe.param.v2.core.AccountLinkCreateParams params =
com.stripe.param.v2.core.AccountLinkCreateParams.builder()
.setAccount(accountId)
.setUseCase(
com.stripe.param.v2.core.AccountLinkCreateParams.UseCase.builder()
.setType(com.stripe.param.v2.core.AccountLinkCreateParams.UseCase.Type.ACCOUNT_ONBOARDING)
.setAccountOnboarding(
com.stripe.param.v2.core.AccountLinkCreateParams.UseCase.AccountOnboarding.builder()
.addConfiguration(com.stripe.param.v2.core.AccountLinkCreateParams.UseCase.AccountOnboarding.Configuration.RECIPIENT)
.setRefreshUrl("https://example.com")
.setReturnUrl("https://example.com?accountId=" + accountId)
.build()
)
.build()
)
.build();
com.stripe.model.v2.core.AccountLink accountLink = v2Client.v2().core().accountLinks().create(params);
return new Gson().toJson(Map.of("url", accountLink.getUrl()));
} catch (StripeException e) {
response.status(500);
return new Gson().toJson(Map.of("error", e.getMessage()));
}
});
// Fetch products for a specific account
get("/api/products/:accountId", (request, response) -> {
String accountId = request.params(":accountId");
try {
List