pi支付流程图:


- <dependency>
- <groupId>com.squareup.okhttp3</groupId>
- <artifactId>okhttp</artifactId>
- <version>4.10.0-RC1</version>
- </dependency>
- <dependency>
- <groupId>cn.hutool</groupId>
- <artifactId>hutool-all</artifactId>
- <version>5.8.0.M4</version>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>4.5.13</version>
- </dependency>
- <dependency>
- <groupId>commons-lang</groupId>
- <artifactId>commons-lang</artifactId>
- <version>2.6</version>
- </dependency>
- import lombok.Data;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.Configuration;
-
- /**
- * 服务器端key
- * @author ThinkPad
- */
- @Configuration
- @Data
- public class CommonConfig {
-
- @Value("${sdk.serverAccessKey}")
- private String serverAccessKey;
- }

loginVO接收pi中心来的用户信息
- import lombok.Data;
-
- /**
- * @author wzx
- * 登录数据封装类
- */
- @Data
- public class LoginVO {
-
- private String userId;
- private String userName;
- private String accessToken;
- }
paymentVO接收支付授权的信息
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- import java.math.BigDecimal;
-
- /**
- * @author ThinkPad
- */
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class PaymentVO {
-
-
-
-
- private String paymentId;
- // 交易金额
- private BigDecimal amount;
- // 名片对应的用户数据
- private String shopUserId;
- // 商品id
- private String shopId;
- // 当前账号用户的id
- private String userId;
-
- }
completeVO接收支付完成的信息
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- /**
- * @author ThinkPad
- */
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class CompleteVO {
- // PI支付ID
-
- private String paymentId;
- // txId
-
- private String txId;
- // 订单ID【余额支付参数】
-
- private String orderId;
- // 支付方式:0:PI钱包 1:余额支付
-
- private String payType;
- }
incompleteVO接收未完成订单的信息
- /**
- * @author ThinkPad
- */
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class IncompleteVO {
-
- private String identifier;
-
-
- private TransactionVO transaction;
- }
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- /**
- * @author ThinkPad
- */
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class TransactionVO {
-
-
- private String txid;
-
-
- private String _link;
- }
发起http请求工具类
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.HttpEntity;
- import org.apache.http.ParseException;
- import org.apache.http.client.methods.CloseableHttpResponse;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.HttpClients;
- import org.apache.http.util.EntityUtils;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.net.HttpURLConnection;
- import java.net.URL;
-
-
- /**
- * @author Ashy.Cheung
- * @http 请求工具类
- * @date 2017.11.10
- */
- public class HttpClientUtil {
-
- public static String sendGet(String url) {
- CloseableHttpClient httpclient = HttpClients.createDefault();
- HttpGet httpget = new HttpGet(url);
- CloseableHttpResponse response = null;
- try {
- response = httpclient.execute(httpget);
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- String result = null;
- try {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- result = EntityUtils.toString(entity);
- }
- } catch (ParseException | IOException e) {
- e.printStackTrace();
- } finally {
- try {
- response.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return result;
- }
-
- /**
- *
- * @param url
- * @param charsetName 返回字符集
- * @return
- */
- public static String sendGet(String url, String charsetName) {
- InputStream inputStream = null;
- HttpURLConnection urlConnection = null;
-
- try {
- URL url1 = new URL(url);
- urlConnection = (HttpURLConnection) url1.openConnection();
- // 将返回的输入流转换成字符串
- inputStream = urlConnection.getInputStream();
- // 指定编码格式
- if (StringUtils.isBlank(charsetName)) {
- charsetName = "UTF-8";
- }
- InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charsetName);
- BufferedReader in = new BufferedReader(inputStreamReader);
- String jsonUserStr = in.readLine();
- return jsonUserStr;
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- try {
- if (null != inputStream) {
- inputStream.close();
- }
- urlConnection.disconnect();
- } catch (Exception e) {
-
- }
- try {
- if (null != urlConnection) {
- urlConnection.disconnect();
- }
-
- } catch (Exception e) {
-
- }
- }
-
- }
- /**
- * 发送HttpPost请求,参数为String
- * 接收端以流形式接收
- */
- public static String sendPost(String url, String param) {
- CloseableHttpClient httpclient = HttpClients.createDefault();
- StringEntity strEntity = null;
- try {
- strEntity = new StringEntity(param, "UTF-8");
- strEntity.setContentType("application/json");
- } catch (Exception e1) {
-
- e1.printStackTrace();
- }
- HttpPost httppost = new HttpPost(url);
- httppost.setEntity(strEntity);
- CloseableHttpResponse response = null;
- String result = null;
- try {
- response = httpclient.execute(httppost);
- HttpEntity entity1 = response.getEntity();
- result = EntityUtils.toString(entity1);
-
- } catch (IOException e) {
- // e.printStackTrace();
- } finally {
- try {
- response.close();
- } catch (Exception e) {
-
- }
- }
-
- return result;
- }
-
- /**
- * 发送不带参数的HttpPost请求
- */
- public static String sendPost(String url) {
- CloseableHttpClient httpclient = HttpClients.createDefault();
- HttpPost httppost = new HttpPost(url);
- CloseableHttpResponse response = null;
- try {
- response = httpclient.execute(httppost);
- } catch (IOException e) {
- e.printStackTrace();
- }
- HttpEntity entity = response.getEntity();
- String result = null;
- try {
- result = EntityUtils.toString(entity);
- } catch (ParseException | IOException e) {
- e.printStackTrace();
- } finally {
- try {
- response.close();
- } catch (Exception e) {
-
- }
- }
- return result;
- }
-
- }
分布式锁工具类
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.data.redis.connection.RedisStringCommands;
- import org.springframework.data.redis.connection.ReturnType;
- import org.springframework.data.redis.core.RedisCallback;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.types.Expiration;
- import org.springframework.stereotype.Component;
-
- import javax.annotation.Resource;
- import java.nio.charset.StandardCharsets;
- @Component
- public class RedisLockUtil {
-
- private static final Logger log = LoggerFactory.getLogger(RedisLockUtil.class);
-
- @Resource
- RedisTemplate
redisTemplate; -
-
-
- /**
- * 释放锁脚本,原子操作,lua脚本
- */
- private static final String UNLOCK_LUA;
-
- static {
- StringBuilder sb = new StringBuilder();
- sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
- sb.append("then ");
- sb.append(" return redis.call(\"del\",KEYS[1]) ");
- sb.append("else ");
- sb.append(" return 0 ");
- sb.append("end ");
- UNLOCK_LUA = sb.toString();
-
- }
-
- /**
- * 获取分布式锁,原子操作
- *
- * @param lockKey 锁
- * @param lockValue 唯一ID
- * @param leaseTime 过期时间 秒
- * @return 是否枷锁成功
- */
- public boolean tryLock(String lockKey, String lockValue, long leaseTime) {
- try {
- RedisCallback
callback = (connection) -> connection.set(lockKey.getBytes(StandardCharsets.UTF_8), - lockValue.getBytes(StandardCharsets.UTF_8), Expiration.seconds(leaseTime),
- RedisStringCommands.SetOption.SET_IF_ABSENT);
- return redisTemplate.execute(callback);
- } catch (Exception e) {
- log.error("redis lock error ,lock key: {}, value : {}, error info : {}", lockKey, lockValue, e);
- }
- return false;
- }
-
- /**
- * 释放锁
- *
- * @param lockKey 锁
- * @param lockValue 唯一ID
- * @return 执行结果
- */
- public boolean unlock(String lockKey, String lockValue) {
- RedisCallback
callback = (connection) -> connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN, 1, lockKey.getBytes(StandardCharsets.UTF_8), lockValue.getBytes(StandardCharsets.UTF_8)); - return redisTemplate.execute(callback);
- }
-
- /**
- * 获取分布式锁,该方法不再使用
- *
- * @param lockKey 锁
- * @param lockValue 唯一ID
- * @param waitTime 等待时间 秒
- * @param leaseTime 过期时间 秒
- * @return 是否枷锁成功
- */
- @Deprecated
- public boolean tryLock(String lockKey, String lockValue, long waitTime, long leaseTime) {
- try {
- RedisCallback
callback = (connection) -> connection.set(lockKey.getBytes(StandardCharsets.UTF_8), - lockValue.getBytes(StandardCharsets.UTF_8), Expiration.seconds(leaseTime),
- RedisStringCommands.SetOption.SET_IF_ABSENT);
- return redisTemplate.execute(callback);
- } catch (Exception e) {
- log.error("redis lock error ,lock key: {}, value : {}, error info : {}", lockKey, lockValue, e);
- }
- return false;
- }
- }
生成uuid工具类
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
- public class UUID {
-
- public static String randomUUID() {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HH'H'mm'M'ss'S'SSS");
- String id = sdf.format(new Date()) + (int) ((Math.random() * 9 + 1) * 100000000) + (int) ((Math.random() * 9 + 1) * 10);
- return id;
- }
-
- public static String randomQr() {
- String id = (int) ((Math.random() * 9 + 1) * 1000) + "-" + (int) ((Math.random() * 9 + 1) * 1000) + "-" + (int) ((Math.random() * 9 + 1) * 1000);
- return id;
- }
-
- }
信息返回的枚举
- import lombok.AllArgsConstructor;
-
- import lombok.Getter;
-
- /**
- * @author ThinkPad
- */
-
- @Getter
- @AllArgsConstructor
- public enum PaymentEnum {
- PAYMENT_ENUM_1(1, "订单不存在","失败"),
- PAYMENT_ENUM_2(2,"订单不是待支付状态","失败"),
- PAYMENT_ENUM_3(3,"支付金额少于订单金额","失败"),
- PAYMENT_ENUM_4(4,"调用太快","失败"),
- PAYMENT_ENUM_5(5,"余额不足,请前往充值","失败"),
- PAYMENT_ENUM_6(6,"支付成功","成功"),
- PAYMENT_ENUM_7(7,"处理成功","失败"),
- PAYMENT_ENUM_8(8,"处理失败","失败");
- private final Integer code;
- private final String msg;
- private final String status;
-
- public static String getMsgByCode(Integer code) {
- for (PaymentEnum value : PaymentEnum.values()) {
- if (value.getCode().equals(code)) {
- return value.getMsg();
- }
- }
- return null;
- }
-
- public static String getStatusByCode(Integer code) {
- for (PaymentEnum value : PaymentEnum.values()) {
- if (value.getCode().equals(code)) {
- return value.getStatus() ;
- }
- }
- return null;
- }
-
-
- }
支付的controller层
- /**
- * 处理未完成的订单 (这部十分重要,会影响到后面的操作)
- */
- @PostMapping("payOrder/incomplete")
- @ApiOperation("处理未完成的订单")
- @ApiImplicitParam(name = "Authorization", value = "传入你的令牌",required = true, dataType = "String",paramType="header")
- public ResponseVO incomplete(@RequestBody IncompleteVO incompleteVO) {
-
- try {
-
- return orderInfoService.incomplete(incompleteVO);
- } catch (Exception e) {
- log.error("报错如下:{}", e.getMessage());
- throw new BusinessException("支付失败,请联系后台人员"+e.getLocalizedMessage()+e.toString()+e.getCause().toString());
- }
-
- }
-
- /**
- * 前端请求支付授权,在本地订单创建后调
- */
- @PostMapping("payOrder/approve")
- @ApiOperation("前端请求支付授权,在本地订单创建后调")
- @ApiImplicitParam(name = "Authorization", value = "传入你的令牌",required = true, dataType = "String",paramType="header")
- public ResponseVO
approve(@RequestBody PaymentVO paymentVO) { -
- try {
-
- String orderId = orderInfoService.approve(paymentVO);
-
- return ResponseVO.getSuccessResponseVo(orderId);
- } catch (Exception e) {
- log.error("报错如下:{}", e.getMessage());
- throw new BusinessException("支付失败,请联系后台人员"+e.getLocalizedMessage()+e.toString()+e.getCause().toString());
- }
-
- }
-
-
- /**
- * 前端支付完成,余额支付直接调用此方法
- */
- @PostMapping("payOrder/complete")
- @ApiOperation("前端支付完成,余额支付直接调用此方法")
- @ApiImplicitParam(name = "Authorization", value = "传入你的令牌",required = true, dataType = "String",paramType="header")
- public ResponseVO complete(@RequestBody CompleteVO completeVO) {
-
- try {
-
-
- return orderInfoService.complete(completeVO);
- } catch (Exception e) {
- log.error("报错如下:{}", e.getMessage());
- throw new BusinessException("支付失败,请联系后台人员"+e.getLocalizedMessage()+e.toString()+e.getCause().toString());
- }
-
- }
-
- /**
- * 取消支付,订单关闭
- */
- @PostMapping("payOrder/cancelled")
- @ApiOperation("取消支付,订单关闭")
- @ApiImplicitParam(name = "Authorization", value = "传入你的令牌",required = true, dataType = "String",paramType="header")
- public ResponseVO
cancelled(@RequestBody String orderId) { -
- try {
-
- Boolean order = orderInfoService.cancelled(orderId);
- // if (!order){throw new BusinessException("取消订单失败");}
- return ResponseVO.getSuccessResponseVo("取消订单成功");
- } catch (Exception e) {
- log.error("报错如下:{}", e.getMessage());
- throw new BusinessException("取消失败,请联系后台人员"+e.getLocalizedMessage()+e.toString()+e.getCause().toString());
- }
-
- }
支付的service层
- /**
- * 请求支付授权,创建order。返回orderId
- * @param paymentVO
- * @return
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public String approve(PaymentVO paymentVO) {
- log.error("approve-------------------------------------------------------------");
- OrderInfo orderInfo;
- log.error("paymentVO----------------------------------"+paymentVO);
- //获取付款信息
- OkHttpClient client = new OkHttpClient();
- Request request = new Request.Builder()
- .url("https://api.minepi.com/v2/payments/" + paymentVO.getPaymentId())
- .addHeader("Authorization", "Key " + commonConfig.getServerAccessKey())
- .build();
- try (Response response = client.newCall(request).execute()) {
- if (!response.isSuccessful()) {
- String string = response.body().string();
- JSONObject jsonObject1 = JSON.parseObject(string);
- log.error("!response-------------------------------------------------------------"+commonConfig.getServerAccessKey());
- throw new RuntimeException("payments error " + jsonObject1.getString("error_message"));
- }
- String string = response.body().string();
- log.error("response-------------------------------------------------------------"+string);
-
- JSONObject jsonObject1 = JSON.parseObject(string);
- //校验实际支付金额
- BigDecimal userFinalPrice = paymentVO.getAmount();
-
-
- if (userFinalPrice.compareTo(jsonObject1.getBigDecimal("amount")) < 0) {
- log.error(userFinalPrice+"response-------------------------------------------------------------"+jsonObject1.getBigDecimal("amount"));
- throw new RuntimeException("支付金额少于订单金额");
- }
-
-
- } catch (Exception e) {
- throw new BusinessException("支付失败,请联系后台人员"+e.getLocalizedMessage()+e.toString()+e.getCause().toString());
- }
-
-
- OkHttpClient client1 = new OkHttpClient();
- //信息真实,通知PI我准备好了,可以付款了
- Request request1 = new Request.Builder()
- .url("https://api.minepi.com/v2/payments/" + paymentVO.getPaymentId() + "/approve")
- .addHeader("Content-Type", "application/json")
- .addHeader("Access-Control-Allow-Origin", "*")
- .addHeader("Authorization", "Key " + commonConfig.getServerAccessKey())
- .post(RequestBody.create("", MediaType.parse("application/json")))
- .build();
- try (Response response1 = client1.newCall(request1).execute()) {
- if (!response1.isSuccessful()) {
- throw new RuntimeException("approve error: ");
- }
- log.error("response1-------------------------------------------------------------");
-
- //更新支付报文
- // tMerStoreGoodsOrderEntity.setPayOrderId(paymentDto.getPaymentId());
- // tMerStoreGoodsOrderEntity.setPayStatusType("10007002");//支付中
- // itMerStoreGoodsOrderService.updateById(tMerStoreGoodsOrderEntity);
- log.error("return-------------------------------------------------------------");
- } catch (RuntimeException | IOException e) {
- log.error("error-------------------------------------------------------------");
- e.printStackTrace();
- }
- // 生成订单
- orderInfo = new OrderInfo();
- orderInfo.setOrderId(StringUtil.generateShortId());
- orderInfo.setShopId(paymentVO.getShopId());
- orderInfo.setUserId(paymentVO.getUserId());
- orderInfo.setShopUserId(paymentVO.getShopUserId());
- orderInfo.setAmount(paymentVO.getAmount());
- orderInfoMapper.insert(orderInfo);
- log.error("生成订单-------------------------------------------------------------");
- return orderInfo.getOrderId();
- }
- /**
- * 前端支付完成,余额支付直接调用此方法
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public ResponseVO complete(CompleteVO completeVO) {
- String payType = completeVO.getPayType();
- log.error("complete------------------------------------------------------------"+completeVO);
- if ("1".equals(payType)) {
- //余额支付
- String orderId = completeVO.getOrderId();
- String lockName = "access:lock:complete:" + orderId;
- String lockId = UUID.randomUUID();
- if (!redisLockUtil.tryLock(lockName, lockId, 20L)) {
- // 调用太快
- return new ResponseVO(PaymentEnum.getStatusByCode(4),4,PaymentEnum.getMsgByCode(4));
- }
- // 获取订单信息
- OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper
().eq("order_id", orderId)); -
- if ((orderInfo.getOrderStatus() != 0)) {
- // 订单不是待支付状态
- return new ResponseVO(PaymentEnum.getStatusByCode(2),2,PaymentEnum.getMsgByCode(2));
- }
- String userId = orderInfo.getUserId();
- AccountInfo accountInfo = accountInfoMapper.selectOne(new QueryWrapper
() - .eq("user_id", userId));
-
- BigDecimal balance = accountInfo.getPiBalance();
- if (balance.compareTo(orderInfo.getAmount()) < 0) {
- // 余额不足,请前往充值
- return new ResponseVO(PaymentEnum.getStatusByCode(5),5,PaymentEnum.getMsgByCode(5));
- }
- int update = orderInfoMapper.update(null,new UpdateWrapper
() - .eq("order_id",orderId)
- .set("order_status",1));
- balance=balance.subtract(orderInfo.getAmount());
- int update1 = accountInfoMapper.update(null, new UpdateWrapper
() - .eq("user_id", userId)
- .set("pi_balance", balance));
- // 支付成功
- return new ResponseVO(PaymentEnum.getStatusByCode(6),6,PaymentEnum.getMsgByCode(6));
- }
- //PI钱包支付
- String paymentId = completeVO.getPaymentId();//PI订单号
- String lockName = "access:lock:complete:" + paymentId;
- String lockId = UUID.randomUUID();
- log.error(paymentId+"-----------------"+lockName+"---------------------"+lockId);
- if (!redisLockUtil.tryLock(lockName, lockId, 20L)) {
- // 调用太快
- log.error("!RedisLockUtil---------------------------------------------------------调用太快");
- return new ResponseVO(PaymentEnum.getStatusByCode(4),4,PaymentEnum.getMsgByCode(4));
- }
- OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper
() - .eq("order_id", completeVO.getOrderId()));
- log.error("orderId--------------------------------------------------------------"+orderInfo);
- if (null == orderInfo) {
- // 订单不存在
- log.error("!orderinfo--------------------------------------------------------不存在");
- return new ResponseVO(PaymentEnum.getStatusByCode(1),1,PaymentEnum.getMsgByCode(1));
- }
- log.error("orderinfo------------------------------------------------------------------"+orderInfo);
- if (orderInfo.getOrderStatus() != 0) {
- // 订单不是待支付状态
- log.error("!order---------------------------------------------------------pay");
- return new ResponseVO(PaymentEnum.getStatusByCode(2),2,PaymentEnum.getMsgByCode(2));
- }
-
-
- //通知PI完成交易
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("txid", completeVO.getTxId());
-
- Map
heads = new HashMap<>(); - heads.put("Content-Type", "application/json;charset=UTF-8");
- heads.put("Authorization", "Key " + commonConfig.getServerAccessKey());
- log.error("pi-----------------------------------------"+jsonObject);
- try {
- HttpResponse response = HttpRequest.post("https://api.minepi.com/v2/payments/" + paymentId + "/complete")
- .headerMap(heads, false)
- .body(String.valueOf(jsonObject))
- .timeout(5 * 60 * 1000)
- .execute();
- String body = response.body();
- JSONObject jsonObject1 = JSON.parseObject(body);
- String error = jsonObject1.getString("error");
- if (!StringUtils.isEmpty(error)) {
- log.error("!strinutils-----------------------------"+body);
- throw new RuntimeException("订单完成异常!");
- }
- orderInfo.setOrderStatus(1);
- // 更新订单
- orderInfoMapper.updateById(orderInfo);
- log.error("支付成功------------------------------------------------------");
- // 支付成功
- return new ResponseVO(PaymentEnum.getStatusByCode(6),6,PaymentEnum.getMsgByCode(6));
- } catch (Exception e) {
- throw e;
- }
-
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Boolean cancelled(String orderId) {
- int update = orderInfoMapper.update(null, new UpdateWrapper
() - .eq("order_id", orderId)
- .set("order_status", 3));
- return update > 0;
- }
-
- @Override
- public ResponseVO incomplete(IncompleteVO incompleteVO) {
- log.error("incomplete--------------------------------");
- try {
- //先处理未完成的订单
- String oldpaymentId = incompleteVO.getIdentifier();
- TransactionVO transaction = incompleteVO.getTransaction();
- log.error("?transation--------------------"+transaction);
- log.error("?oldpaymentId------------------"+oldpaymentId);
- if (null != transaction) {
- log.error("transation--------------------"+transaction);
- log.error("oldpaymentId------------------"+oldpaymentId);
- String txid = transaction.getTxid();
- String txURL = transaction.get_link();
-
- // OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper
().eq("order_id", oldpaymentId)); - // if (null == orderInfo) {
- // log.error("order-----------------null");
- // throw new RuntimeException("旧订单不存在");
- // }
- //
- // if (orderInfo.getOrderStatus()==1) {
- // log.error("orderStatus---------------------"+orderInfo.getOrderStatus());
- // throw new RuntimeException("订单是已支付状态");
- // }
-
- String get = HttpClientUtil.sendGet(txURL);
- JSONObject jsonObject1 = JSON.parseObject(get);
- String piOrderId = jsonObject1.getString("memo");//我方订单ID
-
- log.error("memo---------------------"+piOrderId);
-
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("txid", txid);
-
- Map
heads = new HashMap<>(); - heads.put("Content-Type", "application/json;charset=UTF-8");
- heads.put("Authorization", "Key " + commonConfig.getServerAccessKey());
-
- try {
- HttpResponse response = HttpRequest.post("https://api.minepi.com/v2/payments/" + piOrderId + "/complete")
- .headerMap(heads, false)
- .body(String.valueOf(jsonObject))
- .timeout(5 * 60 * 1000)
- .execute();
- String body = response.body();
- JSONObject jsonObject2 = JSON.parseObject(body);
- String error = jsonObject2.getString("error");
- if (!StringUtils.isEmpty(error)) {
- log.error("!response------------------"+error);
- throw new RuntimeException("订单完成异常!");
- }
-
- return new ResponseVO(PaymentEnum.getStatusByCode(7),7,PaymentEnum.getMsgByCode(7));
- } catch (Exception e) {
- throw e;
- }
- }
- } catch (Exception e) {
- return new ResponseVO(PaymentEnum.getStatusByCode(8),8,PaymentEnum.getMsgByCode(8));
- }
- return new ResponseVO(PaymentEnum.getStatusByCode(8),8,PaymentEnum.getMsgByCode(8));
- }
前端路由
- API
- // 授权
- import request from "@/api/http";
- import axios from "axios";
-
- // 支付授权
- export function payAuth(data){
- return request({
- url: "/api/order/payOrder/approve",
- method: "post",
- data,
- });
- }
-
- //未完成订单
- export function payIncomplete(data){
- return request({
- url: "/api/order/payOrder/incomplete",
- method: "post",
- data,
- });
- }
-
- //支付成功
- export function payDone(data){
- return request({
- url: "/api/order/payOrder/complete",
- method: "post",
- data,
- });
- }
- //支付取消
- export function payCancel(data){
- return request({
- url: "/api/order/payOrder/cancelled",
- method: "post",
- data,
- })
- }
支付前端代码
- test(){
- const pay_message = this.pay_message
- let orderid = ''
- console.log({
- amount: pay_message.amount,
- shopUserId: pay_message.shopUserId,
- shopId: pay_message.shopId,
- userId: pay_message.userId
- })
- Pi.createPayment({
- // Amount of π to be paid:
- amount: 3.14,
- // An explanation of the payment - will be shown to the user:
- memo: "购买特殊数据", // e.g: "Digital kitten #1234",
- // An arbitrary developer-provided metadata object - for your own usage:
- metadata: { productID : 'apple_pie_1' }, // e.g: { kittenId: 1234 }
- }, {
- // Callbacks you need to implement - read more about those in the detailed docs linked below:
-
- // 授权
- async onReadyForServerApproval(paymentId) {
- console.log('paymentId',paymentId)
- payAuth({
- paymentId: paymentId,
- amount: pay_message.amount,
- shopUserId: pay_message.shopUserId,
- shopId: pay_message.shopId,
- userId: pay_message.userId
- }).then((data) => {
- orderid = data.data
- console.log('orderId',orderid)
- }).catch(error => {
- console.log(error)
- })
- },
- //支付成功
- onReadyForServerCompletion: function(paymentId, txid) {
- alert(1111)
- console.log(paymentId, 'paymentId', 'txid', txid,'orderid',orderid )
- payDone({paymentId: paymentId, txId: txid, orderId: orderid,payType:'0'}).then(res => {
- console.log(res)
- // if (res && res.code === 0) {
- // this.payDoneJump();
- // }
- })
- },
- //支付取消
- onCancel: function(orderid) {
- console.log('onCancel' + orderid)
- payCancel(orderid).then((data) => {
- console.log(data)
- })
- },
- //支付失败
- onError: function(error, payment) {console.log('error:',error);console.log('payment:',payment)}
- });
- },
登录自动调用未支付订单,这个十分重要因为会影响支付授权。
- const loginFun = () => {
- Pi.init({ version: "2.0", sandbox: true });
-
- const scopes = ["payments", "username", "wallet_address"];
-
- function onIncompletePaymentFound(payment) {
- alert(1111111)
- console.log("payment", payment);
- return payIncomplete({
- identifier:payment.identifier,
- transaction:{
- _link:payment.transaction._link,
- txid:res.transaction.txid
- }
- })
- }
- Pi.authenticate(scopes, onIncompletePaymentFound).then(function (auth) {
- console.log("auth", auth);
- let userInfo = {
- accessToken: auth.accessToken,
- userId: auth.user.uid,
- userName: auth.user.username,
- };
-
- // userGetPush().then((data) => {
- // console.log(data);
- // userStore().userGetPush = data.data;
- // });
-
- Login(userInfo).then((data) => {
- console.log(data);
-
- if (data.status == "success") {
- // 将用户信息存入pinia
- userStore().userInfoChange(data.data);
-
- // 发布消息到socket Login() 存入userId
- // this.$socket.emit("login", data.data.userInfo.userId);
-
- router.push("/home");
- }
- });
- })
- .catch(function (error) {
- console.error(error);
- });
- };
使用Pi-SDK功能发起支付

由Pi SDK自动调用的回调函数,发出支付批准请求

路由到后端的支付授权接口

后端服务器向Pi服务器发起支付授权
- @Override
- @Transactional(rollbackFor = Exception.class)
- public String approve(PaymentVO paymentVO) {
- log.error("approve-------------------------------------------------------------");
- OrderInfo orderInfo;
- log.error("paymentVO----------------------------------"+paymentVO);
- //获取付款信息
- OkHttpClient client = new OkHttpClient();
- Request request = new Request.Builder()
- .url("https://api.minepi.com/v2/payments/" + paymentVO.getPaymentId())
- .addHeader("Authorization", "Key " + commonConfig.getServerAccessKey())
- .build();
- try (Response response = client.newCall(request).execute()) {
- if (!response.isSuccessful()) {
- String string = response.body().string();
- JSONObject jsonObject1 = JSON.parseObject(string);
- log.error("!response-------------------------------------------------------------"+commonConfig.getServerAccessKey());
- throw new RuntimeException("payments error " + jsonObject1.getString("error_message"));
- }
- String string = response.body().string();
- log.error("response-------------------------------------------------------------"+string);
-
- JSONObject jsonObject1 = JSON.parseObject(string);
- //校验实际支付金额
- BigDecimal userFinalPrice = paymentVO.getAmount();
-
-
- if (userFinalPrice.compareTo(jsonObject1.getBigDecimal("amount")) < 0) {
- log.error(userFinalPrice+"response-------------------------------------------------------------"+jsonObject1.getBigDecimal("amount"));
- throw new RuntimeException("支付金额少于订单金额");
- }
-
-
- } catch (Exception e) {
- throw new BusinessException("支付失败,请联系后台人员"+e.getLocalizedMessage()+e.toString()+e.getCause().toString());
- }
-
-
- OkHttpClient client1 = new OkHttpClient();
- //信息真实,通知PI我准备好了,可以付款了
- Request request1 = new Request.Builder()
- .url("https://api.minepi.com/v2/payments/" + paymentVO.getPaymentId() + "/approve")
- .addHeader("Content-Type", "application/json")
- .addHeader("Access-Control-Allow-Origin", "*")
- .addHeader("Authorization", "Key " + commonConfig.getServerAccessKey())
- .post(RequestBody.create("", MediaType.parse("application/json")))
- .build();
- try (Response response1 = client1.newCall(request1).execute()) {
- if (!response1.isSuccessful()) {
- throw new RuntimeException("approve error: ");
- }
- log.error("response1-------------------------------------------------------------");
-
- //更新支付报文
- // tMerStoreGoodsOrderEntity.setPayOrderId(paymentDto.getPaymentId());
- // tMerStoreGoodsOrderEntity.setPayStatusType("10007002");//支付中
- // itMerStoreGoodsOrderService.updateById(tMerStoreGoodsOrderEntity);
- log.error("return-------------------------------------------------------------");
- } catch (RuntimeException | IOException e) {
- log.error("error-------------------------------------------------------------");
- e.printStackTrace();
- }
- // 生成订单
- orderInfo = new OrderInfo();
- orderInfo.setOrderId(StringUtil.generateShortId());
- orderInfo.setShopId(paymentVO.getShopId());
- orderInfo.setUserId(paymentVO.getUserId());
- orderInfo.setShopUserId(paymentVO.getShopUserId());
- orderInfo.setAmount(paymentVO.getAmount());
- orderInfoMapper.insert(orderInfo);
- log.error("生成订单-------------------------------------------------------------");
- return orderInfo.getOrderId();
- }
PI游览器向用户显示付款详细信息页面,我们等待用户签署交易
由Pi SDK自动调用完成的回调函数

从你的后端服务器到Pi服务器的API请求以完成付款(让pi服务器知道你完成此付款)
- /**
- * 前端支付完成,余额支付直接调用此方法
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public ResponseVO complete(CompleteVO completeVO) {
- String payType = completeVO.getPayType();
- log.error("complete------------------------------------------------------------"+completeVO);
- if ("1".equals(payType)) {
- //余额支付
- String orderId = completeVO.getOrderId();
- String lockName = "access:lock:complete:" + orderId;
- String lockId = UUID.randomUUID();
- if (!redisLockUtil.tryLock(lockName, lockId, 20L)) {
- // 调用太快
- return new ResponseVO(PaymentEnum.getStatusByCode(4),4,PaymentEnum.getMsgByCode(4));
- }
- // 获取订单信息
- OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper<OrderInfo>().eq("order_id", orderId));
-
- if ((orderInfo.getOrderStatus() != 0)) {
- // 订单不是待支付状态
- return new ResponseVO(PaymentEnum.getStatusByCode(2),2,PaymentEnum.getMsgByCode(2));
- }
- String userId = orderInfo.getUserId();
- AccountInfo accountInfo = accountInfoMapper.selectOne(new QueryWrapper<AccountInfo>()
- .eq("user_id", userId));
-
- BigDecimal balance = accountInfo.getPiBalance();
- if (balance.compareTo(orderInfo.getAmount()) < 0) {
- // 余额不足,请前往充值
- return new ResponseVO(PaymentEnum.getStatusByCode(5),5,PaymentEnum.getMsgByCode(5));
- }
- int update = orderInfoMapper.update(null,new UpdateWrapper<OrderInfo>()
- .eq("order_id",orderId)
- .set("order_status",1));
- balance=balance.subtract(orderInfo.getAmount());
- int update1 = accountInfoMapper.update(null, new UpdateWrapper<AccountInfo>()
- .eq("user_id", userId)
- .set("pi_balance", balance));
- // 支付成功
- return new ResponseVO(PaymentEnum.getStatusByCode(6),6,PaymentEnum.getMsgByCode(6));
- }
- //PI钱包支付
- String paymentId = completeVO.getPaymentId();//PI订单号
- String lockName = "access:lock:complete:" + paymentId;
- String lockId = UUID.randomUUID();
- log.error(paymentId+"-----------------"+lockName+"---------------------"+lockId);
- if (!redisLockUtil.tryLock(lockName, lockId, 20L)) {
- // 调用太快
- log.error("!RedisLockUtil---------------------------------------------------------调用太快");
- return new ResponseVO(PaymentEnum.getStatusByCode(4),4,PaymentEnum.getMsgByCode(4));
- }
- OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper<OrderInfo>()
- .eq("order_id", completeVO.getOrderId()));
- log.error("orderId--------------------------------------------------------------"+orderInfo);
- if (null == orderInfo) {
- // 订单不存在
- log.error("!orderinfo--------------------------------------------------------不存在");
- return new ResponseVO(PaymentEnum.getStatusByCode(1),1,PaymentEnum.getMsgByCode(1));
- }
- log.error("orderinfo------------------------------------------------------------------"+orderInfo);
- if (orderInfo.getOrderStatus() != 0) {
- // 订单不是待支付状态
- log.error("!order---------------------------------------------------------pay");
- return new ResponseVO(PaymentEnum.getStatusByCode(2),2,PaymentEnum.getMsgByCode(2));
- }
-
-
- //通知PI完成交易
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("txid", completeVO.getTxId());
-
- Map<String, String> heads = new HashMap<>();
- heads.put("Content-Type", "application/json;charset=UTF-8");
- heads.put("Authorization", "Key " + commonConfig.getServerAccessKey());
- log.error("pi-----------------------------------------"+jsonObject);
- try {
- HttpResponse response = HttpRequest.post("https://api.minepi.com/v2/payments/" + paymentId + "/complete")
- .headerMap(heads, false)
- .body(String.valueOf(jsonObject))
- .timeout(5 * 60 * 1000)
- .execute();
- String body = response.body();
- JSONObject jsonObject1 = JSON.parseObject(body);
- String error = jsonObject1.getString("error");
- if (!StringUtils.isEmpty(error)) {
- log.error("!strinutils-----------------------------"+body);
- throw new RuntimeException("订单完成异常!");
- }
- orderInfo.setOrderStatus(1);
- // 更新订单
- orderInfoMapper.updateById(orderInfo);
- log.error("支付成功------------------------------------------------------");
- // 支付成功
- return new ResponseVO(PaymentEnum.getStatusByCode(6),6,PaymentEnum.getMsgByCode(6));
- } catch (Exception e) {
- throw e;
- }
-
- }
注意,如果用户有未处理的订单,会导致用户重新创建支付失败,需要有个接口去处理未完成的订单
前端一初始化就去执行处理未完成的订单

路由到后端的接口

后端接口代码
- @Override
- public ResponseVO incomplete(IncompleteVO incompleteVO) {
- log.error("incomplete--------------------------------");
- try {
- //先处理未完成的订单
- String oldpaymentId = incompleteVO.getIdentifier();
- TransactionVO transaction = incompleteVO.getTransaction();
- log.error("?transation--------------------"+transaction);
- log.error("?oldpaymentId------------------"+oldpaymentId);
- if (null != transaction) {
- log.error("transation--------------------"+transaction);
- log.error("oldpaymentId------------------"+oldpaymentId);
- String txid = transaction.getTxid();
- String txURL = transaction.get_link();
-
- // OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper<OrderInfo>().eq("order_id", oldpaymentId));
- // if (null == orderInfo) {
- // log.error("order-----------------null");
- // throw new RuntimeException("旧订单不存在");
- // }
- //
- // if (orderInfo.getOrderStatus()==1) {
- // log.error("orderStatus---------------------"+orderInfo.getOrderStatus());
- // throw new RuntimeException("订单是已支付状态");
- // }
-
- String get = HttpClientUtil.sendGet(txURL);
- JSONObject jsonObject1 = JSON.parseObject(get);
- String piOrderId = jsonObject1.getString("memo");//我方订单ID
-
- log.error("memo---------------------"+piOrderId);
-
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("txid", txid);
-
- Map<String, String> heads = new HashMap<>();
- heads.put("Content-Type", "application/json;charset=UTF-8");
- heads.put("Authorization", "Key " + commonConfig.getServerAccessKey());
-
- try {
- HttpResponse response = HttpRequest.post("https://api.minepi.com/v2/payments/" + piOrderId + "/complete")
- .headerMap(heads, false)
- .body(String.valueOf(jsonObject))
- .timeout(5 * 60 * 1000)
- .execute();
- String body = response.body();
- JSONObject jsonObject2 = JSON.parseObject(body);
- String error = jsonObject2.getString("error");
- if (!StringUtils.isEmpty(error)) {
- log.error("!response------------------"+error);
- throw new RuntimeException("订单完成异常!");
- }
-
- return new ResponseVO(PaymentEnum.getStatusByCode(7),7,PaymentEnum.getMsgByCode(7));
- } catch (Exception e) {
- throw e;
- }
- }
- } catch (Exception e) {
- return new ResponseVO(PaymentEnum.getStatusByCode(8),8,PaymentEnum.getMsgByCode(8));
- }
- return new ResponseVO(PaymentEnum.getStatusByCode(8),8,PaymentEnum.getMsgByCode(8));
- }