心情追忆:构建支付模块的五个基本接口设计

news/2024/11/28 9:41:46/

        之前,我独自一人开发了一个名为“心情追忆”的小程序,旨在帮助用户记录日常的心情变化及重要时刻。我从项目的构思、设计、前端小程序)开发、后端搭建到最终部署。经过一个月的努力,通过群聊分享等方式,用户量也有了将近200人。虽然取得了初步的成绩,但我希望小程序能够持续发展。

基于上一篇

​​​​​​​心情追忆 - 打造完美支付体验:从零开始的支付功能实现https://blog.csdn.net/qq_34319145/article/details/144003105

是基于产品思维需要开发支付功能, 上篇只写了用户支付, 但实际支付需要有5个基本接口

1.用户支付

public static PrepayWithRequestPaymentResponse prepay(Integer total, String productTitle, String outTradeNo, String userOpenId) {// 使用单例模式获取配置Config config = getConfigInstance();// 构建serviceJsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();// request.setXxx(val)设置所需参数,具体参数可见Request定义PrepayRequest request = new PrepayRequest();Amount amount = new Amount();// 金额(单位分)amount.setTotal(total);request.setAmount(amount);request.setAppid(appId);request.setMchid(merchantId);// 支付项目名称request.setDescription(productTitle);// 回调地址request.setNotifyUrl(prePayNotifyUrl);// 交易编号request.setOutTradeNo(outTradeNo);Payer payer = new Payer();// 用户openidpayer.setOpenid(userOpenId);request.setPayer(payer);// 调用下单方法,这个是要给前端唤醒支付组件PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = service.prepayWithRequestPayment(request);return prepayWithRequestPaymentResponse;}

2.支付回调

public void payNotify(@RequestHeader("Wechatpay-Signature") String wechatSignature,@RequestHeader("Wechatpay-Serial") String wechatPaySerial,@RequestHeader("Wechatpay-Nonce") String wechatpayNonce,@RequestHeader("Wechatpay-Timestamp") String wechatTimestamp,@RequestHeader("Wechatpay-Signature-Type") String signatureType,@RequestBody String requestBody) {// 打印上面的所有参数, 方便调试log.info("wechatSignature: {}, wechatPaySerial: {}, wechatpayNonce: {}, wechatTimestamp: {}, signatureType: {}, requestBody: {}",wechatSignature, wechatPaySerial, wechatpayNonce, wechatTimestamp, signatureType, requestBody);// 构建RequestParam, 这是下面的回调通知解析器需要用到的参数com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder().serialNumber(wechatPaySerial).nonce(wechatpayNonce).signature(wechatSignature).timestamp(wechatTimestamp).body(requestBody).build();// 初始化 回调通知解析器, 传参和支付的配置一样NotificationParser parser = new NotificationParser(WxPayment.getConfigInstance());// sdk自动 验签、解密并转换成 Transaction(交易信息)Transaction transaction = parser.parse(requestParam, Transaction.class);// 更新支付记录表WechatPaymentRecords wechatPaymentRecord = wechatPaymentRecordsService.findByOutTradeNo(transaction.getOutTradeNo());wechatPaymentRecord.setStatus(transaction.getTradeState().name());wechatPaymentRecord.setTransactionId(transaction.getTransactionId());wechatPaymentRecordsService.updateById(wechatPaymentRecord);// 上面都成功后, 处理业务: 增加用户可使用次数UserFunctionRecord userFunctionRecord = userFunctionRecordService.getUserFunctionRecord(wechatPaymentRecord.getUserId());userFunctionRecord.setTotalUsageLimit(userFunctionRecord.getTotalUsageLimit() + 1);userFunctionRecordService.updateById(userFunctionRecord);// 自己好玩, 发个钉钉消息dingTalkMsg.sendMsgToDingTalk(JSONUtil.toJsonPrettyStr(transaction));}

3.支付列表查询

        这个就是查自己记录的表了

    public BaseResult<List<WechatPaymentRecords>> listPayments(@CurrentUser UserPrincipal userPrincipal) {// 基于springSecurity管理的CurrentUser拿到用户ID, 去查询支付记录List<WechatPaymentRecords> byUserId = wechatPaymentRecordsService.findByUserId(userPrincipal.getUserId());return BaseResult.success(byUserId);}

4.用户退款

和支付差不多, 不过比支付简单的是不用返回唤起支付组件的参数, 随便返回业务需要的参数就好

/*** 退款* @param outTradeNo 内部支付单号* @param outRefundNo 内部退款单号, 自己生成* @param reason 退款原因, 不必填, 业务必须填, 用于用户画像* @param total 订单金额* @param refund 退款金额* @return*/public static Refund createRefund(String outTradeNo,String outRefundNo, String reason, Long total, Long refund) {// 使用单例模式获取配置Config config = getConfigInstance();// 构建serviceRefundService service = new RefundService.Builder().config(config).build();CreateRequest request = new CreateRequest();request.setOutTradeNo(outTradeNo);request.setOutRefundNo(outRefundNo);request.setReason(reason);request.setNotifyUrl(refundNotifyUrl);AmountReq amount = new AmountReq();amount.setTotal(total);amount.setRefund(refund);amount.setCurrency("CNY");request.setAmount(amount);Refund refundResponse = service.create(request);return refundResponse;}

5.退款回调

和支付回调差不多, 只不过解析出来的是RefundNotification退款信息, 支付回调可以和退款回调写相同的一个, 也可以写不同. 我觉得写不同代码看的会少很多分支会简洁一点. 

public void refundNotify(@RequestHeader("Wechatpay-Signature") String wechatSignature,@RequestHeader("Wechatpay-Serial") String wechatPaySerial,@RequestHeader("Wechatpay-Nonce") String wechatpayNonce,@RequestHeader("Wechatpay-Timestamp") String wechatTimestamp,@RequestHeader("Wechatpay-Signature-Type") String signatureType,@RequestBody String requestBody) {// 打印上面的所有参数log.info("wechatSignature: {}, wechatPaySerial: {}, wechatpayNonce: {}, wechatTimestamp: {}, signatureType: {}, requestBody: {}",wechatSignature, wechatPaySerial, wechatpayNonce, wechatTimestamp, signatureType, requestBody);// 构建RequestParamcom.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder().serialNumber(wechatPaySerial).nonce(wechatpayNonce).signature(wechatSignature).timestamp(wechatTimestamp).body(requestBody).build();// 初始化 NotificationParserNotificationParser parser = new NotificationParser(WxPayment.getConfigInstance());// sdk自动 验签、解密并转换成 RefundNotification(退款信息)RefundNotification transaction = parser.parse(requestParam, RefundNotification.class);WechatPaymentRecords wechatPaymentRecord = wechatPaymentRecordsService.findByOutTradeNo(transaction.getOutTradeNo());wechatPaymentRecord.setRefundStatus(transaction.getRefundStatus().name());wechatPaymentRecord.setRefundAmount(transaction.getAmount().getRefund());wechatPaymentRecord.setRefundTime(LocalDateTime.now());wechatPaymentRecord.setOutRefundNo(transaction.getOutRefundNo());wechatPaymentRecord.setRefundId(transaction.getRefundId());wechatPaymentRecordsService.updateById(wechatPaymentRecord);dingTalkMsg.sendMsgToDingTalk(JSONUtil.toJsonPrettyStr(transaction));}

然后就是通用的配置

标准的单例双重验证, 不知道的可以去了解一下单例双重验证

// 提供一个静态方法来获取 Config 实例public static RSAAutoCertificateConfig getConfigInstance() {if (configInstance == null) {synchronized (lock) {if (configInstance == null) {configInstance = new RSAAutoCertificateConfig.Builder().merchantId(merchantId).privateKeyFromPath(privateKeyPath).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3key).build();}}}return configInstance;}

因为支付是购买产品, 所以需要一个产品表product, (有的大公司还有sku的概念, 但我这里不需要, 一切从简)

所以支付表可以这么设计:

关键是, 微信交易(transaction_id)的唯一Id和自己交易编号(out_trade_no)的唯一Id, 和状态, 退款也有一套. 是为了在微信支付平台查询实际交易信息用.

其中要关联业务数据userId和product_id.

CREATE TABLE `wechat_payment_records` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键,自增ID',`transaction_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信支付交易号,用于唯一标识一次支付交易',`out_trade_no` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '商户订单号,由商户生成,用于唯一标识一次支付请求',`user_id` bigint NOT NULL COMMENT '用户ID,关联用户表',`product_id` bigint NOT NULL COMMENT '产品Id',`amount` bigint NOT NULL COMMENT '支付金额,单位为元',`currency` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '货币种类,如CNY',`payment_time` datetime DEFAULT NULL COMMENT '支付时间,记录实际支付成功的时间',`status` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '支付状态,如SUCCESS、FAIL、REFUND等',`payment_method` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '支付方式,如JSAPI、APP、H5等',`description` text COLLATE utf8mb4_general_ci COMMENT '订单描述,用于记录支付的具体内容',`notify_url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '回调通知URL,微信支付回调的地址',`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间,即发起支付请求的时间',`updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间,每次更新记录时自动更新',`ip_address` varchar(45) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户发起支付请求时的IP地址',`device_info` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '设备信息,如设备型号、操作系统等',`refund_status` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '退款状态,如NOT_REFUND、PARTIAL_REFUND、FULL_REFUND等',`refund_amount` bigint DEFAULT NULL COMMENT '退款金额,单位为元',`refund_time` datetime DEFAULT NULL COMMENT '退款时间,记录实际退款成功的时间',`out_refund_no` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '退款编号',`refund_id` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信支付退款单号',`refund_reason` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '退款原因',PRIMARY KEY (`id`),UNIQUE KEY `out_trade_no` (`out_trade_no`),UNIQUE KEY `transaction_id` (`transaction_id`),KEY `idx_user_id` (`user_id`) COMMENT '用户ID索引,用于快速查询某个用户的支付记录',KEY `idx_status` (`status`) COMMENT '支付状态索引,用于快速查询特定状态的支付记录',KEY `idx_created_at` (`created_at`) COMMENT '创建时间索引,用于按时间范围查询支付记录'
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='微信支付记录表';

总结

        和之前一篇一样: 

Java项目部署的三个阶段https://blog.csdn.net/qq_34319145/article/details/143963071目前我只有一个产品就是支付获得分析次数, 所以支付相关的表可以不太多. 如果后续业务变多变复杂, 就再继续去设计, 去完善. 不能一开始就上最完美的方案, 一定是要从最小MVP产品去一步一步迭代才行.

PS:

        上线审核时, 发现IOS打开小程序不允许出现虚拟产品的交易, 所以要关掉iOS的支付功能

// 检查是否为iOS系统const systemInfo = Taro.getSystemInfoSync();if (systemInfo.platform === 'ios') {Taro.showToast({title: 'iOS用户暂不支持支付功能',icon: 'none',duration: 2000});return;}

 

 


http://www.ppmy.cn/news/1550576.html

相关文章

【前端】JavaScript中的柯里化(Currying)详解及实现

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;什么是柯里化&#xff1f;&#x1f4af;柯里化的特点&#x1f4af;柯里化的简单示例&#x1f4af;通用的柯里化实现&#x1f4af;柯里化让代码更易读的原因&#x1f4af…

Excel把其中一张工作表导出成一个新的文件

excel导出一张工作表 一个Excel表里有多个工作表&#xff0c;怎么才能导出一个工作表&#xff0c;让其生成新的Excel文件呢&#xff1f; 第一步&#xff1a;首先打开Excel表格&#xff0c;然后选择要导出的工作表的名字&#xff0c;比如“Sheet1”&#xff0c;把鼠标放到“She…

CentOS上如何离线批量自动化部署zabbix 7.0版本客户端

# CentOS上如何离线批量自动化部署zabbix 7.0版本客户端 管理的服务器大部分都是CentOS操作系统&#xff0c;版本主要是CentOS 7。因为监控服务器需要&#xff0c;要在前两天搭建的Zabbix 7.0系统上把这些CentOS 7系统都监控起来。因为服务器数量众多&#xff0c;而且有些服务…

SJYP 24冬季系列 FROZEN CHARISMA发布

近日&#xff0c;女装品牌SJYP 2024年冬季系列——FROZEN CHARISMA已正式发布&#xff0c;展现了更加干练的法式风格。此次新品发布不仅延续了SJYP一贯的强烈设计风格和个性时尚&#xff0c;更融入了法式风情的干练元素&#xff0c;为消费者带来了一场视觉与穿着的双重盛宴。  …

基于单片机的智慧小区人脸识别门禁系统

本设计基于单片机的智慧小区人脸识别门禁系统。由STM32F103C8T6单片机核心板、显示模块、摄像头模块、舵机模块、按键模块和电源模块组成。可以通过摄像头模块对进入人员人脸数据进行采集&#xff0c;识别成功后&#xff0c;舵机模块动作&#xff0c;模拟门禁打开&#xff0c;门…

11.28周四F34-Day9打卡

文章目录 1. 你需要的是更多的练习。解析答案:【解析答案分析】【拓展内容】2. 跟我说说你昨天买什么了。解析答案:【解析答案分析】3. 他跟你说的是错的。解析答案:【解析答案分析】【对比分析】4. 原因是她没有通过考试。解析答案:【解析答案分析】5. 你说的和他告诉我们…

CTF之密码学(培根密码)

培根密码&#xff0c;又名倍康尼密码&#xff08;Bacons cipher&#xff09;&#xff0c;是由法兰西斯培根发明的一种隐写术&#xff0c;属于密码学领域的一种替换密码。以下是关于培根密码的详细介绍&#xff1a; 一、原理 培根密码本质上是一种二进制密码&#xff0c;但它没…

力扣—102. 二叉树的层序遍历

102. 二叉树的层序遍历 题目&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例&#xff1a; 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输…