--内购支付

This commit is contained in:
yc13649764453
2023-05-25 09:06:04 +08:00
parent 5ed3e9818a
commit 12af476515
14 changed files with 1026 additions and 0 deletions

View File

@@ -0,0 +1,269 @@
package com.peanut.modules.pay.IOSPay;
import lombok.Data;
import org.apache.http.HttpStatus;
import java.io.Serializable;
import java.util.Map;
import java.util.List;
@Data
public class Result<T> implements Serializable {
/**
* 成功标志
*/
private boolean success = true;
/**
* 返回代码
*/
private Integer code = 0;
/**
* 返回处理消息
*/
private String message = "操作成功!";
/**
* 返回数据对象 result
*/
private T result;
/**
* 时间戳
*/
private long timestamp = System.currentTimeMillis();
public Result() {
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public static Result error00(String msg) {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
}
public static Result<Object> error() {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "=================================");
}
public Result<T> error500(String message) {
this.setMessage(message);
this.setCode(500);
this.setSuccess(false);
return this;
}
public static Result<Object> error1() {
Result<Object> r = new Result<Object>();
r.setCode(500);
r.setMessage("bundle有误");
r.setSuccess(false);
return r;
}
public Result<T> success(String message) {
this.setMessage(message);
this.setCode(200);
this.setSuccess(true);
return this;
}
public static Result<Object> ok() {
Result<Object> r = new Result<Object>();
r.setMessage("操作成功");
r.setCode(200);
r.setSuccess(true);
return r;
}
public static Result<Object> updateok() {
Result<Object> r = new Result<Object>();
r.setMessage("修改成功");
r.setCode(200);
r.setSuccess(true);
return r;
}
public static Result<Object> ok0() {
Result<Object> r = new Result<Object>();
r.setMessage("操作成功");
r.setCode(0);
r.setSuccess(true);
return r;
}
public static Result<Object> nook(String msg) {
return error(500, msg);
}
public static Result<Object> nook(int code, String msg) {
Result<Object> r = new Result<Object>();
r.setCode(code);
r.setSuccess(false);
r.setMessage(msg);
return r;
}
public static Result<Object> ok(String msg) {
Result<Object> r = new Result<Object>();
r.setMessage(msg);
r.setCode(200);
r.setSuccess(true);
r.setResult(null);
return r;
}
public static Result<Object> err() {
Result<Object> r = new Result<Object>();
r.setMessage("该笔订单待支付");
r.setCode(500);
r.setSuccess(true);
return r;
}
public static Result<Object> ok(Map data) {
Result<Object> r = new Result<Object>();
r.setCode(200);
r.setMessage("操作成功");
r.setSuccess(true);
r.setResult(data);
return r;
}
public static Result<Object> ok(Map data,String msg) {
Result<Object> r = new Result<Object>();
r.setCode(200);
r.setMessage(msg);
r.setSuccess(true);
r.setResult(data);
return r;
}
public static Result<Object> ok(List data) {
Result<Object> r = new Result<Object>();
r.setCode(200);
r.setSuccess(true);
r.setResult(data);
return r;
}
public static Result<List<?>> okl(List<?> data) {
Result<List<?>> r = new Result<List<?>>();
r.setCode(200);
r.setSuccess(true);
r.setResult(data);
return r;
}
public static Result<Object> ok(Object data) {
Result<Object> r = new Result<Object>();
r.setCode(200);
r.setSuccess(true);
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
return error(403, msg);
}
public static Result<Object> error(int code, String msg) {
Result<Object> r = new Result<Object>();
r.setCode(code);
r.setSuccess(false);
r.setMessage(msg);
return r;
}
public static Result<Object> error(Map data,String msg) {
Result<Object> r = new Result<Object>();
r.setCode(403);
r.setMessage(msg);
r.setSuccess(false);
r.setResult(data);
return r;
}
/**
* 无权限访问返回结果
*/
public static Result<Object> noauth(String msg) {
return error(401, msg);
}
/**
* 无权限访问返回结果
*/
public static Result<List<?>> noauth() {
Result<List<?>> r = new Result<List<?>>();
r.setCode(401);
r.setSuccess(false);
r.setMessage("您没有该接口的权限!");
return r;
}
// public static Result<Object> ok() {
// Result<Object> r = new Result<Object>();
}

View File

@@ -0,0 +1,23 @@
package com.peanut.modules.pay.IOSPay.constant;
public interface VerifyReceiptConstant {
/**
* ios 端app bundle id
* 也可以配置在nacos里面,动态获取
* URL_SANDBOX 沙盒测试
* URL_VERIFY 正式
*/
String APP_BUNDLE_IDENTIFIER = "com.cn.nuttyreading";
String URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";
String URL_VERIFY = "https://sandbox.itunes.apple.com/verifyReceipt";
// String URL_VERIFY = "https://buy.itunes.apple.com/verifyReceipt";
}

View File

@@ -0,0 +1,12 @@
package com.peanut.modules.pay.IOSPay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.peanut.modules.pay.IOSPay.model.entities.IosPayOrderEntity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface PayIOSOrderMapper extends BaseMapper<IosPayOrderEntity> {
}

View File

@@ -0,0 +1,13 @@
package com.peanut.modules.pay.IOSPay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.peanut.modules.pay.IOSPay.model.entities.IosPayOrderEntity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface PayPaymentOrderMapper extends BaseMapper<IosPayOrderEntity> {
}

View File

@@ -0,0 +1,19 @@
package com.peanut.modules.pay.IOSPay.model.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class GenOrderReqDTO implements Serializable {
/*
* 用户id
* */
String id;
/*
*商品id
* */
String productId;
String OrderId;
}

View File

@@ -0,0 +1,46 @@
package com.peanut.modules.pay.IOSPay.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.models.auth.In;
import lombok.Data;
import javax.persistence.Column;
import java.io.Serializable;
@Data
public class IapRequestDTO implements Serializable {
/**
* 订单id
*/
String orderId;
/**
* 苹果内购生成的交易id,对应SwiftyStoreKit-PaymentTransaction-transactionIdentifier
*/
@JsonProperty("transactionId")
String transactionId;
/**
* base64 字符串
*/
@Column(name = "receiptData")
String receiptData;
boolean isSandBox;
String relevanceoid;
String productId;
String customerOid;
String Body;
// String subject;
//o 成功 1失败
Integer failureflag;
}

View File

@@ -0,0 +1,54 @@
package com.peanut.modules.pay.IOSPay.model.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class IapResponseDTO {
private String environment;
private AppleReceipt receipt;
private String status;
@Data
static public class AppleReceipt implements Serializable {
private String receipt_type;
private Long adam_id;
private Long app_item_id;
private String bundle_id;
private String application_version;
private Long download_id;
private Long version_external_identifier;
private String receipt_creation_date;
private String receipt_creation_date_ms;
private String receipt_creation_date_pst;
private String request_date;
private String request_date_ms;
private String request_date_pst;
private String original_purchase_date;
private String original_purchase_date_ms;
private String original_purchase_date_pst;
private String original_application_version;
private AppleOrder[] in_app;
}
@Data
public static class AppleOrder implements Serializable {
private String quantity;
private String product_id;
private String transaction_id;
private String original_transaction_id;
private String purchase_date;
private String purchase_date_ms;
private String purchase_date_pst;
private String original_purchase_date;
private String original_purchase_date_ms;
private String original_purchase_date_pst;
private String is_trial_period;
private String in_app_ownership_type;
}
}

View File

@@ -0,0 +1,169 @@
package com.peanut.modules.pay.IOSPay.model.entities;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.Value;
import org.joda.time.DateTime;
import javax.persistence.Column;
import javax.persistence.Id;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("pay_apple_order")
public class IosPayOrderEntity implements Serializable {
/**
* id
*/
@TableId(value = "id")
private int id;
/**
*订单号的唯一标识*/
@TableField("orderid")
private String orderid;
/**
* 购买的商品ID
*/
@TableField("productID")
private String productID;
/**
*用户ID
*/
@TableField("customerOid")
private String customerOid;
/**
*订单交易凭证
*/
@TableField("authToken")
private String receiptData;
/**
*创建订单时间
*/
@TableField("create_time")
private Date createTime;
/**
*订单交易状态 0-交易成功 1-交易失败
*/
@TableField("order_status")
private String orderStatus;
/**
*充值金额
*/
@TableField("money")
private Integer money;
/**
*实际到账金额
*/
@TableField("point")
private Integer point;
/**
* 轮询十次失败标记
*/
@TableField("failureflag")
private Integer failureflag;
/**
*
*删除标记 1 -未删除 0-已删除
*/
@TableField("del_flag")
private Integer delFlag;
/**
*0-未关单 1-已关单 @Column(name = "close_order")
*/
@TableField("close_order")
private Integer closeOrder;
/**
* 充值金额
*/
@TableField("recharge_Amount")
private BigDecimal rechargeAmount;
/**
* 充值渠道
*/
@TableField("recharge_Channel")
private String rechargeChannel;
/**
* 实际充值金额
*/
@TableField("realAmount")
private BigDecimal realAmount;
/**
* 充值状态
*/
@TableField("recharge_Status")
private String rechargeStatus;
/**
* 支付成功时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date successTime;
@TableField("Body")
private String Body;
/*
商品描述
* */
@TableField("username")
private String username;
/*
标识内购商品和相关操作的ID
* */
@TableField("relevanceoid")
private String relevanceoid;
/*
买方支付的金额包括税和折扣
* */
@TableField("buyerPay_Amount")
private String buyerPayAmount;
@TableField("TransactionId")
private String transactionId;
}

View File

@@ -0,0 +1,14 @@
package com.peanut.modules.pay.IOSPay.service;
import com.peanut.modules.pay.IOSPay.model.dto.IapResponseDTO;
import org.springframework.stereotype.Service;
@Service
public interface IapVerifyReceiptService {
/**
* 苹果凭证二次校验
* @param receipt 苹果凭证
* @param isSandBox 是否是沙盒环境
* @return 校验结果
*/
IapResponseDTO verifyIapReceipt(String receipt, boolean isSandBox);
}

View File

@@ -0,0 +1,37 @@
package com.peanut.modules.pay.IOSPay.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.peanut.common.utils.PageUtils;
import com.peanut.common.utils.R;
import com.peanut.modules.book.entity.BuyOrderEntity;
import com.peanut.modules.pay.IOSPay.model.dto.IapRequestDTO;
import com.peanut.modules.pay.IOSPay.model.entities.IosPayOrderEntity;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public interface OrderService extends IService<IosPayOrderEntity> {
IosPayOrderEntity genOrder(String id, String productId);
/**
*
*
* @param failureflag 失败标识
* @param
* @param
* @return
*/
String failure(String failureflag, String orderid);
PageUtils queryPage1(Map<String, Object> params) throws Exception;
}

View File

@@ -0,0 +1,25 @@
package com.peanut.modules.pay.IOSPay.service.impl;
import com.alibaba.fastjson.JSON;
import com.peanut.modules.pay.IOSPay.constant.VerifyReceiptConstant;
import com.peanut.modules.pay.IOSPay.model.dto.IapResponseDTO;
import com.peanut.modules.pay.IOSPay.service.IapVerifyReceiptService;
import com.peanut.modules.pay.IOSPay.utils.IapRequestUtils;
import org.springframework.stereotype.Service;
@Service
public class IapVerifyReceiptServiceImpl implements IapVerifyReceiptService {
@Override
public IapResponseDTO verifyIapReceipt(String receipt, boolean isSandBox) {
String url = isSandBox ? VerifyReceiptConstant.URL_SANDBOX : VerifyReceiptConstant.URL_VERIFY;
// 发送receipt给苹果服务器
// Object o = new Object(String.valueOf("MIIUKAYJKoZIhvcNAQcCoIIUGTCCFBUCAQExCzAJBgU"));
// String receipt1= "MIIUKAYJKoZIhvcNAQcCoIIUGTCCFBUCAQExCzAJBgUrDgMCGgUAMIIDZgYJKoZIhvcNAQcBoIIDVwSCA1MxggNPMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQECAQEEAwIBADALAgELAgEBBAMCAQAwCwIBDwIBAQQDAgEAMAsCARACAQEEAwIBADALAgEZAgEBBAMCAQMwDAIBCgIBAQQEFgI0KzAMAgEOAgEBBAQCAgCJMA0CAQMCAQEEBQwDMTEwMA0CAQ0CAQEEBQIDAf4oMA0CARMCAQEEBQwDMS4wMA4CAQkCAQEEBgIEUDI2MDAYAgEEAgECBBAX4ibAXjZ7IAwLx10evzedMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQUYpDtGI14h";
String res = IapRequestUtils.sendVerifyReceiptRequest(url, receipt);
IapResponseDTO iapResponseDTO = JSON.parseObject(res, IapResponseDTO.class);
return iapResponseDTO;
}
}

View File

@@ -0,0 +1,150 @@
package com.peanut.modules.pay.IOSPay.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.peanut.common.utils.ExcludeEmptyQueryWrapper;
import com.peanut.common.utils.PageUtils;
import com.peanut.common.utils.Query;
import com.peanut.modules.app.service.UserService;
import com.peanut.modules.book.entity.BookBuyConfigEntity;
import com.peanut.modules.book.entity.BuyOrderEntity;
import com.peanut.modules.book.entity.MyUserEntity;
import com.peanut.modules.book.service.BookBuyConfigService;
import com.peanut.modules.book.service.BuyOrderService;
import com.peanut.modules.book.service.MyUserService;
import com.peanut.modules.pay.IOSPay.mapper.PayIOSOrderMapper;
import com.peanut.modules.pay.IOSPay.mapper.PayPaymentOrderMapper;
import com.peanut.modules.pay.IOSPay.model.entities.IosPayOrderEntity;
import com.peanut.modules.pay.IOSPay.service.OrderService;
import com.peanut.modules.pay.IOSPay.vo.FailureVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class OrderServiceImpl extends ServiceImpl<PayIOSOrderMapper,IosPayOrderEntity> implements OrderService {
@Autowired
private PayPaymentOrderMapper payPaymentOrderMapper;
@Autowired
private PayIOSOrderMapper payIOSOrderMapper;
@Autowired
private BuyOrderService buyOrderService;
@Autowired
private UserService userService;
@Autowired
private MyUserService myUserService;
@Autowired
private BookBuyConfigService bookBuyConfigService;
@Override
public IosPayOrderEntity genOrder(String id, String productId) {
IosPayOrderEntity insertEntity = new IosPayOrderEntity();
insertEntity.setProductID(productId);
insertEntity.setRechargeChannel("后台手动充值");
insertEntity.setRechargeStatus("");
insertEntity.setId(Integer.valueOf(id));
insertEntity.setRealAmount(BigDecimal.ZERO);
insertEntity.setRechargeAmount(BigDecimal.ZERO);
payIOSOrderMapper.insert(insertEntity);
return insertEntity;
}
@Override
public String failure(String failureflag, String orderid) {
return null;
}
@Override
public PageUtils queryPage1(Map<String, Object> params) throws Exception {
List<IosPayOrderEntity> orders = payIOSOrderMapper.selectList(new QueryWrapper<IosPayOrderEntity>().eq("failureflag", 1));
IosPayOrderEntity iosPayOrderEntity = new IosPayOrderEntity();
IPage<IosPayOrderEntity> page = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String startTime = null;
String endTime = null;
if (params.containsKey("startTime")) {
startTime = sdf.format(Long.parseLong(params.get("startTime").toString()));
endTime = sdf.format(Long.parseLong(params.get("endTime").toString()));
}
if (params.get("failureflag") != null ) {
page = this.page(
new Query<IosPayOrderEntity>().getPage(params),
new ExcludeEmptyQueryWrapper<IosPayOrderEntity>().eq("failureflag", 1).eq("orderid", params.get("key")).like("username", params.get("key"))
.apply(startTime != null, "date_format (create_time,'%Y-%m-%d') >= date_format ({0},'%Y-%m-%d')", startTime)
.apply(endTime != null, "date_format (create_time,'%Y-%m-%d') <= date_format ({0},'%Y-%m-%d')", endTime).orderByDesc("create_time")
);
} else {
page = this.page(
new Query<IosPayOrderEntity>().getPage(params),
new ExcludeEmptyQueryWrapper<IosPayOrderEntity>().eq("failureflag",1).eq("failureflag", params.get("key"))
.apply(startTime != null, "date_format (create_time,'%Y-%m-%d') >= date_format ({0},'%Y-%m-%d')", startTime)
.apply(endTime != null, "date_format (create_time,'%Y-%m-%d') <= date_format ({0},'%Y-%m-%d')", endTime).orderByDesc("create_time")
);
}
List<FailureVo> results = new ArrayList<FailureVo>();
for (IosPayOrderEntity order : orders) {
if (0!=order.getFailureflag()) {
FailureVo vo = new FailureVo();
vo.setId(order.getId());
vo.setOrderid(order.getOrderid());
vo.setProductID(order.getProductID());
vo.setCustomerOid(order.getCustomerOid());
vo.setReceiptData(order.getReceiptData());
vo.setCreateTime(order.getCreateTime());
vo.setFailureflag(order.getFailureflag());
vo.setTransactionId(order.getTransactionId());
MyUserEntity user = this.myUserService.getById(Integer.valueOf(order.getCustomerOid()));
if (null == user) {
vo.setUsername("");
} else {
vo.setUsername(user.getName());
}
BookBuyConfigEntity bookBuyConfigEntity = this.bookBuyConfigService.getById(Integer.valueOf(null == order.getProductID() ? "0" : order.getProductID()));
vo.setRealMoney(null == bookBuyConfigEntity ? "0" : bookBuyConfigEntity.getRealMoney());
BuyOrderEntity orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", order.getOrderid()));
if (null != orderEntity) {
vo.setPaymentMethod(orderEntity.getPaymentMethod());
}
results.add(vo);
}
}
return new PageUtils(page);
}
}

View File

@@ -0,0 +1,77 @@
package com.peanut.modules.pay.IOSPay.utils;
import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class IapRequestUtils {
static public String sendVerifyReceiptRequest(String url, String receipt) {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{ new TrustAnyTrustManager() },new java.security.SecureRandom());
URL console = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection)console.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.setRequestMethod("POST");
conn.setRequestProperty("content-type", "application/json");
conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
conn.setDoInput(true);
conn.setDoOutput(true);
String str = String.format("{\"receipt-data\":\"%s\"}", receipt);//拼成固定的格式传给平台
try (OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream())) {
out.write(str);
out.flush();
}
try (InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
} catch (Exception e) {
// log.error("苹果服务器验证出错:{}",e.getMessage());
System.out.println("苹果服务器验证出错:{}"+e.getMessage());
}
return "ok";
}
/**
*
*/
private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
}

File diff suppressed because one or more lines are too long