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

server/2024/11/28 11:53:18/

        之前,我独自一人开发了一个名为“心情追忆”的小程序,旨在帮助用户记录日常的心情变化及重要时刻。我从项目的构思、设计、前端小程序)开发、后端搭建到最终部署。经过一个月的努力,通过群聊分享等方式,用户量也有了将近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/server/145615.html

相关文章

《并查集算法详解及实用模板》

《重生我要成为并查集高手&#x1f354;&#x1f354;&#x1f354;》 并查集&#xff1a;快速查询和快速合并&#xff0c; 路径压缩&#xff0c; 按大小&#xff0c;高度&#xff0c;秩合并。 静态数组实现 &#x1f607;前言 在数据的海洋中&#xff0c;有一种悄然流淌的力量…

Python 将彩色视频转换为黑白视频(MP4-格式可选)

以下是使用 Python 将彩色视频转换为黑白电视风格的示例代码&#xff0c;主要借助了opencv-python库来实现&#xff1a; python import cv2def convert_to_black_and_white_video(input_video_path, output_video_path):# 打开视频文件cap cv2.VideoCapture(input_video_pat…

机器学习模型——线性回归

文章目录 前言1.基础概念2.代价函数3.单变量线性回归3.1加载数据3.2初始化超参数3.3梯度下降算法3.3.1初次梯度下降3.3.2 多次梯度下降3.3.3结果可视化 前言 随着互联网数据不断累积&#xff0c;硬件不断升级迭代&#xff0c;在这个信息爆炸的时代&#xff0c;机器学习已被应用…

嵌入式linux C++通用makefile模板

编译生成spdlog_app可执行程序 #Makefile # 编译器 #COMPLITEarm-none-linux-gnueabi- #COMPLITE/root/share/nuvoton_cross/arm_linux_4.8/bin/arm-nuvoton-linux-uclibceabi- #/root/share/nuvoton_cross/host/usr/bin/ COMPLITEarm-nuvoton-linux-gnueabi- CC$(COMPLITE)gc…

redis中的bigkey及读取优化

一、bigKey介绍 1、简介 在 Redis 中,Big Key(大键)指的是占用大量内存的单个键。通常,Redis 是一个高性能的内存数据库,但是当某些键变得非常大时,会带来性能上的影响。例如,大量的内存消耗、长时间的操作延迟,甚至可能导致 Redis 停止响应或崩溃。 通俗的来说,指…

macOS 开发环境配置与应用开发指南

macOS 开发环境配置与应用开发指南 macOS作为苹果公司推出的操作系统&#xff0c;因其稳定性、优雅的用户界面和强大的开发支持&#xff0c;已成为开发者和创意专业人士的首选平台之一。无论是开发iOS、macOS桌面应用&#xff0c;还是Web应用、跨平台程序&#xff0c;macOS都提…

【Linux网络编程】TCP套接字

TCP与UDP的区别&#xff1a; udp是无连接的、面向数据报&#xff08;通信时以数据报为单位传输&#xff09;的传输层通信协议&#xff0c;其中每个数据报都是独立的&#xff0c;通信之前不需要建立连接&#xff0c;bind绑定套接字后直接可以进行通信。 tcp是面向连接的、基于字…

Python 爬虫入门教程:从零构建你的第一个网络爬虫

网络爬虫是一种自动化程序&#xff0c;用于从网站抓取数据。Python 凭借其丰富的库和简单的语法&#xff0c;是构建网络爬虫的理想语言。本文将带你从零开始学习 Python 爬虫的基本知识&#xff0c;并实现一个简单的爬虫项目。 1. 什么是网络爬虫&#xff1f; 网络爬虫&#x…