Spring Boot集成Stripe快速入门demo

2024-09-01 02:36

本文主要是介绍Spring Boot集成Stripe快速入门demo,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.什么是Stripe?

一体化全球支付平台,开启收入增长引擎,针对不同规模业务打造的支付解决方案,满足从初创公司到跨国企业的多维度需求,助力全球范围内线上线下付款。

  • 转化更多客户: 通过内置的优化功能、100 多种支付方式及一键结账来提高转化率。实现线上和线下付款一体化,提供无缝的客户体验。
  • 全球覆盖,本地体验: 引入多样化支付方式,采用当地货币呈现价格,以此快速拓展新市场,实现向 195+ 个国家/地区的跨境销售,有效降低管理多币种的成本。
  • 减少欺诈,增加收入: Stripe 的机器学习优化功能基于数十亿数据点进行深度训练,为您自动降低欺诈风险,同时提升交易授权率。
  • 降本增效,快速拓展: 通过提高开发人员的工作效率,节省时间和工程资源。凭借领先的可靠性避免宕机,并通过替代支付方式和路径降低成本。

2.代码工程

实验目标

1.实现一次支付功能
2.实现订阅支付功能

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springboot-demo</artifactId><groupId>com.et</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>stripe</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.sparkjava</groupId><artifactId>spark-core</artifactId><version>2.9.4</version></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.9.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><scope>provided</scope></dependency><dependency><groupId>com.stripe</groupId><artifactId>stripe-java</artifactId><version>25.7.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency></dependencies>
</project>

controller

package com.et.stripe.controller;import com.et.stripe.common.Response;
import com.et.stripe.service.StripeService;import com.stripe.model.Coupon;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class PaymentController {@Value("${stripe.keys.public}")private String API_PUBLIC_KEY;private StripeService stripeService;public PaymentController(StripeService stripeService) {this.stripeService = stripeService;}@GetMapping("/")public String homepage() {return "homepage";}@GetMapping("/subscription")public String subscriptionPage(Model model) {model.addAttribute("stripePublicKey", API_PUBLIC_KEY);return "subscription";}@GetMapping("/charge")public String chargePage(Model model) {model.addAttribute("stripePublicKey", API_PUBLIC_KEY);return "charge";}/*========== REST APIs for Handling Payments ===================*/@PostMapping("/create-subscription")public @ResponseBodyResponse createSubscription(String email, String token, String plan, String coupon) {//validate dataif (token == null || plan.isEmpty()) {return new Response(false, "Stripe payment token is missing. Please, try again later.");}//create customer firstString customerId = stripeService.createCustomer(email, token);if (customerId == null) {return new Response(false, "An error occurred while trying to create a customer.");}//create subscriptionString subscriptionId = stripeService.createSubscription(customerId, plan, coupon);if (subscriptionId == null) {return new Response(false, "An error occurred while trying to create a subscription.");}// Ideally you should store customerId and subscriptionId along with customer object here.// These values are required to update or cancel the subscription at later stage.return new Response(true, "Success! Your subscription id is " + subscriptionId);}@PostMapping("/cancel-subscription")public @ResponseBodyResponse cancelSubscription(String subscriptionId) {boolean status = stripeService.cancelSubscription(subscriptionId);if (!status) {return new Response(false, "Failed to cancel the subscription. Please, try later.");}return new Response(true, "Subscription cancelled successfully.");}@PostMapping("/coupon-validator")public @ResponseBodyResponse couponValidator(String code) {Coupon coupon = stripeService.retrieveCoupon(code);if (coupon != null && coupon.getValid()) {String details = (coupon.getPercentOff() == null ? "$" + (coupon.getAmountOff() / 100) : coupon.getPercentOff() + "%") +" OFF " + coupon.getDuration();return new Response(true, details);} else {return new Response(false, "This coupon code is not available. This may be because it has expired or has " +"already been applied to your account.");}}@PostMapping("/create-charge")public @ResponseBodyResponse createCharge(String email, String token) {//validate dataif (token == null) {return new Response(false, "Stripe payment token is missing. Please, try again later.");}//create chargeString chargeId = stripeService.createCharge(email, token, 999); //$9.99 USDif (chargeId == null) {return new Response(false, "An error occurred while trying to create a charge.");}// You may want to store charge id along with order informationreturn new Response(true, "Success! Your charge id is " + chargeId);}
}

service

package com.et.stripe.service;import com.stripe.Stripe;
import com.stripe.model.Charge;
import com.stripe.model.Coupon;
import com.stripe.model.Customer;
import com.stripe.model.Subscription;
import com.stripe.param.SubscriptionCancelParams;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class StripeService {@Value("${stripe.keys.secret}")private String API_SECRET_KEY;public StripeService() {}public String createCustomer(String email, String token) {String id = null;try {Stripe.apiKey = API_SECRET_KEY;Map<String, Object> customerParams = new HashMap<>();// add customer unique id here to track them in your web applicationcustomerParams.put("description", "Customer for " + email);customerParams.put("email", email);customerParams.put("source", token); // ^ obtained with Stripe.js//create a new customerCustomer customer = Customer.create(customerParams);id = customer.getId();} catch (Exception ex) {ex.printStackTrace();}return id;}public String createSubscription(String customerId, String plan, String coupon) {String id = null;try {Stripe.apiKey = API_SECRET_KEY;Map<String, Object> item = new HashMap<>();item.put("price", plan);Map<String, Object> items = new HashMap<>();items.put("0", item);Map<String, Object> params = new HashMap<>();params.put("customer", customerId);params.put("items", items);//add coupon if availableif (!coupon.isEmpty()) {params.put("coupon", coupon);}Subscription sub = Subscription.create(params);id = sub.getId();} catch (Exception ex) {ex.printStackTrace();}return id;}public boolean cancelSubscription(String subscriptionId) {boolean status;try {Stripe.apiKey = API_SECRET_KEY;Subscription sub = Subscription.retrieve(subscriptionId);sub.cancel((SubscriptionCancelParams) null);status = true;} catch (Exception ex) {ex.printStackTrace();status = false;}return status;}public Coupon retrieveCoupon(String code) {try {Stripe.apiKey = API_SECRET_KEY;return Coupon.retrieve(code);} catch (Exception ex) {ex.printStackTrace();}return null;}public String createCharge(String email, String token, int amount) {String id = null;try {Stripe.apiKey = API_SECRET_KEY;Map<String, Object> chargeParams = new HashMap<>();chargeParams.put("amount", amount);chargeParams.put("currency", "usd");chargeParams.put("description", "Charge for " + email);chargeParams.put("source", token); // ^ obtained with Stripe.js//create a chargeCharge charge = Charge.create(chargeParams);id = charge.getId();} catch (Exception ex) {ex.printStackTrace();}return id;}}

templates

homepage

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Homepage</title><!--Bootstrap 4 CSS--><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"><!--Bootstrap 4 JavaScript--><script src="https://code.jquery.com/jquery-3.3.1.min.js"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body class="bg-light pt-5"><!--hero section-->
<section class="py-5"><div class="container"><div class="row"><div class="col-lg-7 col-md-10 col-12 my-auto mx-auto text-center"><h1>Stripe Payment Examples</h1><p class="lead mb-4">What would you like to do?</p><a class="btn btn-primary" th:href="@{/subscription}">Create Recurring Subscription</a><a class="btn btn-success" th:href="@{/charge}">Create One-Time Charge</a><p class="mt-5 text-muted"><small>An example project by <a target="_blank"th:href="@{https://attacomsian.com}">Atta</a>.</small></p></div></div></div>
</section></body>
</html>

charge

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Charge</title><!--Bootstrap 4 CSS--><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"><!--Bootstrap 4 JavaScript--><script src="https://code.jquery.com/jquery-3.3.1.min.js"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script><!--Stripe JavaScript Library--><script src="https://js.stripe.com/v3/"></script>
</head>
<body class="bg-light pt-5"><!--hero section-->
<section class="py-5"><div class="container"><div class="row"><div class="col-lg-6 col-md-8 col-12 my-auto mx-auto"><h1>Stripe One-Time Charge</h1><p class="lead mb-4">Please fill the form below to complete the order payment</p><div class="card mb-4"><div class="card-body"><h5>Leather Bag</h5><p class="lead">USD 9.99</p></div></div><form action="#" id="payment-form" method="post"><input id="api-key" th:value="${stripePublicKey}" type="hidden"><div class="form-group"><label class="font-weight-medium" for="card-element">Enter credit or debit card below</label><div class="w-100" id="card-element"><!-- A Stripe Element will be inserted here. --></div></div><div class="form-group"><input class="form-control" id="email" name="email"placeholder="Email Address" required type="email"></div><!-- Used to display Element errors. --><div class="text-danger w-100" id="card-errors" role="alert"></div><div class="form-group pt-2"><button class="btn btn-primary btn-block" id="submitButton" type="submit">Pay With Your Card</button><div class="small text-muted mt-2">Pay securely with Stripe. By clicking the button above, you agreeto our <a href="#" target="_blank">Terms of Service</a>,<a href="#" target="_blank">Privacy</a> and<a href="#" target="_blank">Refund</a> policies.</div></div></form><p class="mt-5 text-muted"><small>An example project by <a target="_blank" th:href="@{https://attacomsian.com}">Atta</a>.</small></p></div></div></div>
</section><!--custom javascript for handling subscription-->
<script>$(function () {var API_KEY = $('#api-key').val();// Create a Stripe client.var stripe = Stripe(API_KEY);// Create an instance of Elements.var elements = stripe.elements();// Create an instance of the card Element.var card = elements.create('card');// Add an instance of the card Element into the `card-element` <div>.card.mount('#card-element');// Handle real-time validation errors from the card Element.card.addEventListener('change', function (event) {var displayError = document.getElementById('card-errors');if (event.error) {displayError.textContent = event.error.message;} else {displayError.textContent = '';}});// Handle form submission.var form = document.getElementById('payment-form');form.addEventListener('submit', function (event) {event.preventDefault();// handle paymenthandlePayments();});//handle card submissionfunction handlePayments() {stripe.createToken(card).then(function (result) {if (result.error) {// Inform the user if there was an error.var errorElement = document.getElementById('card-errors');errorElement.textContent = result.error.message;} else {// Send the token to your server.var token = result.token.id;var email = $('#email').val();$.post("/create-charge",{email: email, token: token},function (data) {alert(data.details);}, 'json');}});}});
</script></body>
</html>

subscription

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Subscription</title><!--Bootstrap 4 CSS--><link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"><!--Bootstrap 4 JavaScript--><script src="https://code.jquery.com/jquery-3.3.1.min.js"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script><!--Stripe JavaScript Library--><script src="https://js.stripe.com/v3/"></script>
</head>
<body class="bg-light pt-5"><!--hero section-->
<section class="py-5"><div class="container"><div class="row"><div class="col-lg-6 col-md-8 col-12 my-auto mx-auto"><h1>Stripe Recurring Subscription</h1><p class="lead mb-4">Please fill the form below to complete the payment</p><h5 class="mb-2">Choose your payment plan</h5><p class="text-muted">60% OFF when you upgrade to annual plan.</p><div class="py-2"><div class="custom-control custom-radio"><input class="custom-control-input" id="monthly-plan" name="premium-plan" type="radio"value="price_1PtRC5KYHXnPEBSj85nrvuKC"/><label class="custom-control-label" for="monthly-plan"><strong>Monthly $9.99</strong><br/><small class="text-muted">Pay $9.99 every month and get access to all premium features.</small></label></div><div class="custom-control custom-radio mt-3"><input checked="" class="custom-control-input" id="annually-plan" name="premium-plan"type="radio" value="annual-subscription"/><label class="custom-control-label" for="annually-plan"><strong>Yearly $49.99</strong><span class="badge badge-primary ml-1">60% OFF</span><br/><small class="text-muted">Pay $49.99 every year and get access to all premium features.</small></label></div></div><form action="#" id="payment-form" method="post"><input id="api-key" th:value="${stripePublicKey}" type="hidden"><div class="form-group"><label class="font-weight-medium" for="card-element">Enter credit or debit card below</label><div class="w-100" id="card-element"><!-- A Stripe Element will be inserted here. --></div></div><div class="form-group"><input class="form-control" id="email" name="email"placeholder="Email Address" required type="email"></div><div class="form-group"><input class="form-control" id="coupon" name="coupon"placeholder="Coupon code (optional)" type="text"></div><!-- Used to display Element errors. --><div class="text-danger w-100" id="card-errors" role="alert"></div><div class="form-group pt-2"><button class="btn btn-primary btn-block" id="submitButton" type="submit">Pay With Your Card</button><div class="small text-muted mt-2">Pay securely with Stripe. By clicking the button above, you agreeto our <a href="#" target="_blank">Terms of Service</a>,<a href="#" target="_blank">Privacy</a> and<a href="#" target="_blank">Refund</a> policies.</div></div></form><p class="mt-5 text-muted"><small>An example project by <a target="_blank" th:href="@{https://attacomsian.com}">Atta</a>.</small></p></div></div></div>
</section><!--custom javascript for handling subscription-->
<script>$(function () {var API_KEY = $('#api-key').val();// Create a Stripe client.var stripe = Stripe(API_KEY);// Create an instance of Elements.var elements = stripe.elements();// Create an instance of the card Element.var card = elements.create('card');// Add an instance of the card Element into the `card-element` <div>.card.mount('#card-element');// Handle real-time validation errors from the card Element.card.addEventListener('change', function (event) {var displayError = document.getElementById('card-errors');if (event.error) {displayError.textContent = event.error.message;} else {displayError.textContent = '';}});// Handle form submission.var form = document.getElementById('payment-form');form.addEventListener('submit', function (event) {event.preventDefault();//validate coupon if anyvar code = $('#coupon').val().trim();if (code.length > 0) {$.post("/coupon-validator",{code: code},function (data) {if (data.status) {handlePayments();} else {alert(data.details);}}, 'json');} else {handlePayments();}});//handle card submissionfunction handlePayments() {stripe.createToken(card).then(function (result) {if (result.error) {// Inform the user if there was an error.var errorElement = document.getElementById('card-errors');errorElement.textContent = result.error.message;} else {// Send the token to your server.var token = result.token.id;var plan = $('input[name="premium-plan"]:checked').val();var email = $('#email').val();var coupon = $('#coupon').val();$.post("/create-subscription",{email: email, token: token, plan: plan, coupon: coupon},function (data) {alert(data.details);}, 'json');}});}});
</script></body>
</html>

application.yaml

#Stripe keys - REPLACE WITH ACTUAL KEYS
stripe.keys.public=pk_test_xxxxx
stripe.keys.secret=sk_test_xxxxx
#Don't cache thymeleaf files - FOR TEST PURPOSE ONLY
spring.thymeleaf.cache=false

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • GitHub - Harries/springboot-demo: a simple springboot demo with some components for example: redis,solr,rockmq and so on.(stripe)

3.测试

启动Spring Boot应用

一次支付测试

http://127.0.0.1:8080/charge

charge

可以在这个地址 Test card numbers | Stripe Documentation,找到测试卡号, 输入测试卡号,显示支付成功

charge-success

订阅支付测试

http://127.0.0.1:8080/subscription

subscription

输入测试卡号,显示订阅成功(注意:是订阅产品上创建多个价格,而不是创建多个产品)

subscription-success

stripe控制台交易信息

transactions

4.引用

  • Test card numbers | Stripe Documentation
  • Stripe | Financial Infrastructure to Grow Your Revenue
  • Spring Boot集成Stripe快速入门demo | Harries Blog™

这篇关于Spring Boot集成Stripe快速入门demo的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1125697

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听