1.什么是Stripe?
一体化全球支付平台,开启收入增长引擎,针对不同规模业务打造的支付解决方案,满足从初创公司到跨国企业的多维度需求,助力全球范围内线上线下付款。
- 转化更多客户: 通过内置的优化功能、100 多种支付方式及一键结账来提高转化率。实现线上和线下付款一体化,提供无缝的客户体验。
- 全球覆盖,本地体验: 引入多样化支付方式,采用当地货币呈现价格,以此快速拓展新市场,实现向 195+ 个国家/地区的跨境销售,有效降低管理多币种的成本。
- 减少欺诈,增加收入: Stripe 的机器学习优化功能基于数十亿数据点进行深度训练,为您自动降低欺诈风险,同时提升交易授权率。
- 降本增效,快速拓展: 通过提高开发人员的工作效率,节省时间和工程资源。凭借领先的可靠性避免宕机,并通过替代支付方式和路径降低成本。
2.代码工程
实验目标
1.实现一次支付功能 2.实现订阅支付功能
pom.xml
springboot-demo
com.et
1.0-SNAPSHOT
4.0.0
stripe
8
8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-autoconfigure
org.springframework.boot
spring-boot-starter-test
test
com.sparkjava
spark-core
2.9.4
com.google.code.gson
gson
2.9.1
org.projectlombok
lombok
1.18.20
provided
com.stripe
stripe-java
25.7.0
org.springframework.boot
spring-boot-starter-thymeleaf
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 @ResponseBody
Response createSubscription(String email, String token, String plan, String coupon) {
//validate data
if (token == null || plan.isEmpty()) {
return new Response(false, "Stripe payment token is missing. Please, try again later.");
}
//create customer first
String customerId = stripeService.createCustomer(email, token);
if (customerId == null) {
return new Response(false, "An error occurred while trying to create a customer.");
}
//create subscription
String 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 @ResponseBody
Response 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 @ResponseBody
Response 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 @ResponseBody
Response createCharge(String email, String token) {
//validate data
if (token == null) {
return new Response(false, "Stripe payment token is missing. Please, try again later.");
}
//create charge
String chargeId = stripeService.createCharge(email, token, 999); //$9.99 USD
if (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 information
return 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 customerParams = new HashMap<>();
// add customer unique id here to track them in your web application
customerParams.put("description", "Customer for " + email);
customerParams.put("email", email);
customerParams.put("source", token); // ^ obtained with Stripe.js
//create a new customer
Customer 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 item = new HashMap<>();
item.put("price", plan);
Map items = new HashMap<>();
items.put("0", item);
Map params = new HashMap<>();
params.put("customer", customerId);
params.put("items", items);
//add coupon if available
if (!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 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 charge
Charge charge = Charge.create(chargeParams);
id = charge.getId();
} catch (Exception ex) {
ex.printStackTrace();
}
return id;
}
}
templates
homepage
Homepage
Stripe Payment Examples
What would you like to do?
Create Recurring Subscription
Create One-Time Charge
An example project by Atta.
charge
Charge
Stripe One-Time Charge
Please fill the form below to complete the order payment
Leather Bag
USD 9.99
An example project by Atta.
subscription
Subscription
Stripe Recurring Subscription
Please fill the form below to complete the payment
Choose your payment plan
60% OFF when you upgrade to annual plan.
An example project by Atta.
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
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- https://github.com/Harries/springboot-demo(stripe)
3.测试
启动Spring Boot应用
一次支付测试
http://127.0.0.1:8080/charge
可以在这个地址
https://docs.stripe.com/testing,找到测试卡号, 输入测试卡号,显示支付成功
订阅支付测试
http://127.0.0.1:8080/subscription
输入测试卡号,显示订阅成功(注意:是订阅产品上创建多个价格,而不是创建多个产品)
stripe控制台交易信息
4.引用
- https://docs.stripe.com/testing
- https://stripe.com/
- http://www.liuhaihua.cn/archives/711254.html