做过国际支付的应该对stripe都不陌生吧,今天来实战一下支付的流程
<!--stripe-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.3</version>
</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>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>22.5.1</version>
</dependency>
import com.bithelp.annotation.AnonymousAccess;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.*;
import com.stripe.model.checkout.Session;
import com.stripe.net.RequestOptions;
import com.stripe.param.PriceListParams;
import com.stripe.param.PriceUpdateParams;
import com.stripe.param.checkout.SessionCreateParams;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
@RequestMapping("/api/stripe")
public class StripeController {
static {
// 管理平台里面的密钥 详情请看 demo-image/1.jpg ,图片地址链接: https://dashboard.stripe.com/test/apikeys
Stripe.apiKey = "sk_test_51OEkcYEWc6AdwtprMoGUtFwcJu4h2dgVxMJs0PL8eHbWwYc0Vl3kNMgeC9OoZ3bE6gy0kiZSdKk5REOhAzVt89uG00gnnHyJ8P";
}
/**
* 去支付
* 1、创建产品
* 2、设置价格
* 3、创建支付信息 得到url
* https://checkout.stripe.com/c/pay/cs_test_a1ayts7NA3EKscl8e0CtRlx6U3YrYwmxXddf5vr5h3xtifbD6dhPZUg2QI#fidkdWxOYHwnPyd1blpxYHZxWjA0SkJBcGBNR2swUUo0YjRjZklrfDVGPVFXUl1jUGtGZGNhcWBqQE5nVmZ8amp3Ulw1SUdtQmlOb2NCY0xmVkRHf21cQ0pEczFjTG8zdlFGXGdqf3wybWdgNTVCYXFScndkfScpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl
* @return
*/
@PostMapping("/pay")
@AnonymousAccess
public String pay() throws StripeException {
/* 这里生成随机价格 1000-9000的价格*/
int max=9000;
int min=1000;
Random random = new Random();
int randMoney = random.nextInt(max)%(max-min+1) + min;
//创建产品 https://stripe.com/docs/api/products/create
Map<String, Object> params = new HashMap<>();
params.put("name", "产品名称-1");
Product product = Product.create(params);
//创建价格 https://stripe.com/docs/api/prices/create
// Map<String, Object> recurring = new HashMap<>();
// recurring.put("interval", "month");
Map<String, Object> params2 = new HashMap<>();
params2.put("unit_amount", randMoney);
params2.put("currency", "usd");
// params2.put("recurring", recurring);
params2.put("product", product.getId());
Price price = Price.create(params2);
System.out.println(price.toString());
//创建支付信息 得到url
SessionCreateParams params3 = SessionCreateParams.builder()
.setMode(SessionCreateParams.Mode.PAYMENT)
.setSuccessUrl("http://127.0.0.1:9900/success")
.setCancelUrl( "http://127.0.0.1:9900/cancel")
.addLineItem(
SessionCreateParams.LineItem.builder()
.setQuantity(1L)
.setPrice(price.getId())
.build()).putMetadata("orderId",UUID.randomUUID().toString())
.build();
Session session = Session.create(params3);
return session.getUrl();
}
@PostMapping("/pay2")
public String pay2() throws StripeException {
try {
/*PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
.setAmount(1000L) // 支付金额,以分为单位
.setCurrency("usd") // 支付货币类型
.setReceiptEmail("517861659@qq.com") //支付成功发送邮箱
.setDescription("描述~~~123456789")
.build();
PaymentIntent paymentIntent = PaymentIntent.create(params);
System.out.println(paymentIntent.toString());*/
Map<String, Object> params2 = new HashMap<>();
params2.put("amount", 2000);
params2.put("currency", "USD");
params2.put("description", "描述ssss");
params2.put("receipt_email", "517861659@qq.com");
Map<String, Object> automaticPaymentMethods = new HashMap<>();
automaticPaymentMethods.put("enabled", true);
params2.put("automatic_payment_methods", automaticPaymentMethods);
Map<String, Object> metadata = new HashMap<>();
metadata.put("orderId", "2222222222222222");
params2.put("metadata", metadata);
PaymentIntent paymentIntent2 = PaymentIntent.create(params2);
System.out.println(paymentIntent2.toString());
return paymentIntent2.getClientSecret();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
*
* @param request
* @param response
* @return
* @throws StripeException
*/
@PostMapping("/pay3")
public String pay3( HttpServletRequest request, HttpServletResponse response) throws StripeException {
try {
String token = request.getParameter("token");// 支付卡号,测试卡号 的token
// 创建 Charge 对象
Map<String, Object> chargeParams = new HashMap<>();
chargeParams.put("amount", 2000);
chargeParams.put("currency", "usd");
chargeParams.put("source", token);
chargeParams.put("description", "描述..........................");
RequestOptions requestOptions = RequestOptions.builder().setApiKey(Stripe.apiKey).build();
// 发送 Charge 请求并获取响应
Charge charge = Charge.create(chargeParams, requestOptions);
System.out.println(charge);
// 处理成功支付的响应
if (charge.getPaid()) {
// TODO: 处理支付成功的逻辑
return charge.getReceiptUrl();
} else {
// TODO: 处理支付失败的逻辑
return null;
}
}catch (Exception e){
// TODO: 处理异常情况的逻辑
e.printStackTrace();
}
return null;
}
/**
* 基本上会用到的api
* 创建产品
* 修改产品
* 创建产品的价格
* 修改产品的价格(注意这里,修改产品的价格是去把价格存档,然后再新增一条价格数据)
* 得到这个产品的价格列表
* 创建支付信息 得到url
* @return
*/
public void aa() throws StripeException {
int max=9000;
int min=1000;
Random random = new Random();
int randMoney = random.nextInt(max)%(max-min+1) + min;
System.out.println(randMoney);
//创建产品 https://stripe.com/docs/api/products/create
Map<String, Object> params = new HashMap<>();
params.put("name", "Gold Special");
Product product = Product.create(params);
System.out.println(product.toString());
//修改产品 https://stripe.com/docs/api/products/update
Product product2 = Product.retrieve(product.getId());
Map<String, Object> params2 = new HashMap<>();
params2.put("name", product.getName()+randMoney);
Product updatedProduct = product2.update(params2);
System.out.println(updatedProduct.toString());
//创建价格 https://stripe.com/docs/api/prices/create
Map<String, Object> recurring = new HashMap<>();
recurring.put("interval", "month");
Map<String, Object> params3 = new HashMap<>();
params3.put("unit_amount", randMoney);
params3.put("currency", "usd");
params3.put("recurring", recurring);
params3.put("product", product.getId());
Price price = Price.create(params3);
System.out.println(price.toString());
//修改价格 (新建price,老的价格不用,存档) https://stripe.com/docs/products-prices/manage-prices?dashboard-or-api=api#edit-price
Price resource = Price.retrieve(price.getId());
PriceUpdateParams params4 = PriceUpdateParams.builder().setLookupKey(price.getLookupKey()).setActive(false).build();
Price price2 = resource.update(params4);
System.out.println(price2);
//重新创建价格 https://stripe.com/docs/api/prices/create
int randMoney3 = random.nextInt(max)%(max-min+1) + min;
System.out.println(randMoney3);
Map<String, Object> recurring2 = new HashMap<>();
recurring2.put("interval", "month");
Map<String, Object> params5 = new HashMap<>();
params5.put("unit_amount", randMoney3);
params5.put("currency", "usd");
params5.put("recurring", recurring2);
params5.put("product", product.getId());
Price price3 = Price.create(params5);
System.out.println(price3.toString());
//得到这个产品的价格列表
PriceListParams params6 = PriceListParams.builder().setProduct(product.getId()).setActive(true).build();
PriceCollection prices = Price.list(params6);
System.out.println(prices.toString());
//创建支付信息 得到url
SessionCreateParams params7 =
SessionCreateParams.builder()
.setMode(SessionCreateParams.Mode.SUBSCRIPTION)
.setSuccessUrl("http://127.0.0.1:9900/success")
.setCancelUrl( "http://127.0.0.1:9900/cancel")
.addLineItem(
SessionCreateParams.LineItem.builder()
.setQuantity(1L)// 购买数量
.setPrice(price3.getId())// 购买价格
.build())
.putMetadata("orderId",UUID.randomUUID().toString())//订单id 支付成功后,回调的时候拿到这个订单id,进行修改数据库状态
.build();
Session session = Session.create(params7);
System.out.println(session.toString());
System.out.println("最后去支付的url:"+session.getUrl());
}
@RequestMapping("/success")
public String success(){
return "success";
}
@RequestMapping("/cancel")
public String cancel(){
return "cancel";
}
}
回调接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bithelp.annotation.AnonymousAccess;
import com.stripe.model.Event;
import com.stripe.model.EventDataObjectDeserializer;
import com.stripe.net.Webhook;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
@Slf4j
@Controller
public class StripeBackController {
//webhook秘钥签名,存在配置文件中(例如stripe: webhook-key: whsec_fb49bf5262c924f7749602b7fb2ddd1efadbb40f10d675323525255ee01bccf)
@Value("${stripe.webhook-key}")
private String endpointSecret;
@ResponseBody
@RequestMapping(value = {"/api/pay/stripe/stripe_events"}, method = RequestMethod.POST)
@AnonymousAccess
public void stripe_events(HttpServletRequest request) {
System.out.println("------进入回调了------");
try {
InputStream inputStream = request.getInputStream();
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024*4];
int n;
while (-1 != (n = inputStream.read(buffer))) {
output.write(buffer, 0, n);
}
byte[] bytes = output.toByteArray();
String payload = new String(bytes, StandardCharsets.UTF_8);
log.info("支付回调payload体:"+payload);
String sigHeader = request.getHeader("Stripe-Signature");
Event event = Webhook.constructEvent(payload, sigHeader, endpointSecret);//验签,并获取事件
EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
//回调成功取到数据后需要插入订单表一条订单数据;然后更新一次订单表的订单数据;
// 根据钱包地址更新MemberInfo中的状态,根据钱包地址更新保单
String rawJson = dataObjectDeserializer.getRawJson();
JSONObject obj = JSON.parseObject(rawJson);
Long created = obj.getLong("created");
Date date = new Date(created);
OrderRecordQueryCriteria criteria=new OrderRecordQueryCriteria();
switch(event.getType()) {
case "invoice.payment_succeeded":
JSONObject lines = obj.getJSONObject("lines");
JSONArray data = lines.getJSONArray("data");
JSONObject dataOne = data.getJSONObject(0);
JSONObject price = dataOne.getJSONObject("price");
JSONObject recurring = price.getJSONObject("recurring");
// 按年还是按月
String interval = recurring.getString("interval");
String customer_email = obj.getString("customer_email");
String id = obj.get("id").toString();
// 自行存库
break;
case "checkout.session.completed":
String accountAddress=null;
JSONArray arr = obj.getJSONArray("custom_fields");
String invoice = obj.get("invoice").toString();
for (int i = 0; i < arr.size(); i++){
JSONObject jsonObject = arr.getJSONObject(i);
String key = jsonObject.getString("key");
// 取自定义字段
if ("walletaddressthesame21412tedaddress".equals(key)){
accountAddress = jsonObject.getJSONObject("text").getString("value");
}
}
// 自行存库
break;
default:
break;
}
} catch (Exception e) {
log.error("stripe异步通知(webhook事件)",e);
}
}
<!DOCTYPE html>
<html>
<head>
<title>Stripe Payment Demo</title>
</head>
<body>
<div>
<h1>支付方式1 创建商品并去支付</h1>
<button type="button" onclick="toPay()">创建商品并去支付</button><br><br>
</div>
<div>----------------------------------------------分割线---------------------------------------------------------</div>
<div>
<h1>支付方式2 自定义金额支付</h1>
<strong>这里得填写卡号信息,卡号在这里获取测试的 https://stripe.com/docs/testing 我这里用的是 4242424242424242 这个卡号</strong>
<div id="card-element"></div>
<button id="pay-button">Pay $10.00</button>
</div>
<div>----------------------------------------------分割线---------------------------------------------------------</div>
<div>
<h1>支付方式3 自定义金额支付</h1>
<strong>这里得填写卡号信息,卡号在这里获取测试的 https://stripe.com/docs/testing 我这里用的是 4242424242424242 这个卡号</strong>
<form id="payment-form">
<div>
<label for="card-number">Card Number:</label>
<div id="card-number"></div>
</div>
<div>
<label for="card-expiry">Expiration Date:</label>
<div id="card-expiry"></div>
</div>
<div>
<label for="card-cvc">CVC:</label>
<div id="card-cvc"></div>
</div>
<button id="pay-button3" type="submit">Pay $10.00</button>
</form>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script src="/jquery.min.js"></script>
<script>
/*支付方式1 begin */
function toPay() {
$.ajax({
type : 'post',
url : '/pay',
data : {},
success : function(res) {
alert(res)
window.location.href = res;
},
error : function(res) {
alert("网络错误");
}
});
}
/*支付方式1 end */
/*支付方式2 begin */
// 设置Stripe的publishable key
const stripe = Stripe('pk_test_51OEkcYEWc6AdwtprW0NPPa7Dk8762SH8BRYYnZAYTyweb6BkB9sYNCSnQDsad1kZ08MHjqt1u3VUFhXJX9VLnvxUElD008vSYCVxg');
// 创建card元素
const cardElement = stripe.elements().create('card');
// 将card元素添加到DOM中
cardElement.mount('#card-element');
// 监听支付按钮的点击事件
document.getElementById('pay-button').addEventListener('click', async(event) => {
// 禁用支付按钮
event.target.disabled = true;
$.ajax({
type : 'post',
url : '/pay2',
data : {},
success : function(res) {
console.log(res);
// 使用Stripe.js确认支付
stripe.confirmCardPayment(res, {
payment_method: {
card: cardElement
}
}).then(function(result) {
alert("result---"+JSON.stringify(result));
if (result.error) {
// 处理错误
window.location.href = "/cancel";
} else {
// 处理成功
window.location.href = "/success";
}
});
},
error : function(res) {
alert("网络错误");
}
})
})
/*支付方式2 end */
/*支付方式3 begin */
var stripe2 = Stripe('pk_test_51OGDueHBn5TO1g1fcLny0C8TRWXfUnCafdteoEKbScyoorWY0LBhGlKjfGfIcSABzhYFOAv4fIj6sTCYbozy7hbe00GdtWwrax');
var elements = stripe2.elements();
var cardNumberElement = elements.create('cardNumber');
var cardExpiryElement = elements.create('cardExpiry');
var cardCvcElement = elements.create('cardCvc');
cardNumberElement.mount('#card-number');
cardExpiryElement.mount('#card-expiry');
cardCvcElement.mount('#card-cvc');
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe2.createToken(cardNumberElement).then(function(result) {
if (result.error) {
// 处理错误
alert(result.error.message);
} else {
// Token 创建成功,将其传递给后端代码
var token = result.token.id;
console.log(token)
$.ajax({
type : 'post',
url : '/pay3',
data : {"token":token},
success : function(res) {
alert(res)
window.location.href = res;
},
error : function(res) {
alert("网络错误");
}
});
}
});
});
/*支付方式3 end */
</script>
</body>
</html>
测试卡号官网地址:https://stripe.com/docs/testing?locale=zh-CN
选择测试模式,然后点击开发人员
点击开发人员之后选择webhook,按照提示安装脚手架和配置监听路径
创建测试支付链接
选择创建产品后创建好链接
复制链接,准备支付
输入相关链接和测试卡号就可以测试了