[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

[fastadmin] 第三十九篇 FastAdmin easypay yansonga 支付宝支付 回调 验签名错误 bug解决

错误演示

  1. 配置好pay之后,支付都是好的

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

  1. 但是支付宝付款 回调 进行代码打印输出,发现是验签错误

一般这里是走的是v2,v3最新的,但是包肯定没普及那么多。要知道还有很多得支付,使用商户pid,以及key进行发起的呢?这种都是2007 时候常用的支付代码了。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

点进去 verify

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

再点进去 support类

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

找到最终执行代码地方

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

3.验签错误,进行支付宝开放平台密钥工具,验签。

得到params 参数,注意这里参数,你可以通过 支付宝,查询trade_no,进行查询 人家发送给你的回调通知。
地址在这:

验签工具:opensupport.alipay.com/support/FAQ...

  1. 阿里支付转人工 提工单。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决
[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

  1. 工单记录给你们看看,证明一下,总结就是阿里客服公钥是对的,我本地生成就是错的。找了4个客服,对了4次。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

客服直接让我用它给我发的公钥。。。不要管证书解析,这是我就疑问,是不是我工具生成证书完全不对?!注意了,这是一个埋点!

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

开始思考,寻找解决办法

  1. 采用阿里官方sdk,发现还是不行

  2. 采用python ,写一个秒级 更新订单状态

py用的多的是下面这个包
github.com/fzlee/alipay/blob/maste...

阿里云官方pyhton 是这个包,但是我感觉写的太重了,肯定是pip的包好用。
github.com/fzlee/alipay/blob/maste...

python代码:


#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import os

import time

import logging

import mysql.connector

from datetime import datetime

from alipay import AliPay

import json

# 配置日志

logging.basicConfig(

 level=logging.INFO,

 format='%(asctime)s [%(levelname)s] %(message)s',

 handlers=[

        logging.FileHandler("alipay_order_check.log"),

        logging.StreamHandler()

    ]

)

logger = logging.getLogger(__name__)

# 数据库配置

DB_CONFIG = {

 'host': 'localhost',

 'user': 'test',

 'password': 'test',  # 替换为你的数据库密码

 'database': 'test',  # 替换为你的数据库名称

 'port': '3306',

 'pool_name': 'order_pool',

 'pool_size': 10,

 'use_pure': True,

}

# 支付宝配置

ALIPAY_CONFIG = {

 'app_id': '202100testtest890',  # 替换为你的应用ID

 'app_private_key_string': """-----BEGIN RSA PRIVATE KEY-----

MIIEpQIBAAKCAQEAtlryQ9iWi8UmTi9CJfTYzTvCA4urWTjf9CX+/esj+luP9P8R

n6iD2xJfBRdA7k4saDpNuELv7/pFkztIGkaBqag+5fQdrspK3A8+iOjB41ebRk2U

H3bpOKf
6VHYa+IQIeF+D/k4s2DJdi3H3nADNM6qp3XNCQIDAQABAoIBAQCRbojmORcfk3UQ

R7peoR5C19TMlRhryOM7SQ5LHbwMz/dIciBxD6lRDx6+5aPAKpJZ8Z3IRYIsBpJ0

cIP5iJ+iWe8H4B/IJcd8J6A8+jyffl/0uqxWR7SKnbjnevZTkRNxahvnjw6R9hyQ

4gBtdnSp0M6pQ0pdzfveC8pi7tr9U8WVtYIB7UpKV36+YfgjPpu8sYFl1a/8Iea1

5k4zyuKVQrmD4LutLjv8L/WXBeossnMSKsiY5E4HpyGvSVvTJD0RHFZ+5F0tGG1C

tLIZj+sLvx6gGlkrQq2pIMossKQBJeXro4v02BdiqDIiYPyiUXJGWeh/4FpefXJt
ZelEtvvBH8P6LGGjFHVXZ4Qk4oNg4hyxP

rMrRrynNLgL7Z9HTd20D+Sc97LugjMeeYzhCzru

zR9VWPm+OiAo1JuzbU7zwwghJuuyr05sShzJ2t9wdtBLv9c+oGZGR58=

-----END RSA PRIVATE KEY-----

""",

 # 'alipay_public_key_path': '../addons/epay/certs/alipayCertPublicKey_RSA2.crt',  # 替换为支付宝公钥路径

 'alipay_public_key_string': """-----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlryQ9iWi8UmTi9CJfTY

aB9n7cyTGgpJYd1FZwZybK5sTCBIP2ezNJJSFu2S/0BLR+1DxT/UYxnoAD45v18N

G1AHtqakLmervaOtB17XfFX8oQPn6VHYa+IQIeF+D/k4s2DJdi3H3nADNM6qp3XN

CQIDAQAB

-----END PUBLIC KEY-----

""",  # 替换为支付宝公钥路径

 'sign_type': 'RSA2',

 'debug': False # 生产环境设为False

}

# 初始化支付宝客户端

def  init_alipay_client():

 try:

        logger.info("初始化支付宝客户端...")

 # 使用密钥字符串模式初始化

alipay_client = AliPay(

 appid=ALIPAY_CONFIG['app_id'],

 app_private_key_string=ALIPAY_CONFIG['app_private_key_string'],

 alipay_public_key_string=ALIPAY_CONFIG['alipay_public_key_string'],

 sign_type=ALIPAY_CONFIG['sign_type'],

 debug=ALIPAY_CONFIG['debug']

        )

        logger.info("支付宝客户端初始化成功")

 return alipay_client

 except  Exception  as e:

        logger.error(f"支付宝客户端初始化失败: {e}")

 import traceback

        logger.error(f"错误详情: {traceback.format_exc()}")

 raise

# 获取未支付的订单

def  get_unpaid_orders(db_conn):

 try:

cursor = db_conn.cursor(dictionary=True)

query =  """

            SELECT id, order_sn, transactionid, paytype, method, amount, createtime, expiretime

            FROM fa_shop_order

            WHERE paystate = 0 AND orderstate = 0 AND paytype = 'alipay' AND deletetime IS NULL

            AND goodsprice = 0.01

            AND year IS NOT NULL

            ORDER BY createtime DESC

            LIMIT 100

        """

        cursor.execute(query)

orders = cursor.fetchall()

        cursor.close()

 return orders

 except  Exception  as e:

        logger.error(f"获取未支付订单失败: {e}")

 raise

# 查询支付宝订单状态 - 修改后的版本,增加详细日志

def  query_alipay_order_status(order_sn, alipay_client):

 try:

        logger.info(f"准备查询订单状态: {order_sn}")

 # 准备查询参数

query_params = {'out_trade_no': order_sn}

        logger.info(f"查询参数: {query_params}")

 # 执行查询

result = alipay_client.api_alipay_trade_query(out_trade_no="order_sn")

 if result.get("trade_status", "") ==  "TRADE_SUCCESS":

paid =  True

 print("not paid...")

result = alipay_client.api_alipay_trade_query(**query_params)

        logger.info(f"查询结果: {result}")

 if result.get('code') ==  '10000':

 return {

 'success': True,

 'trade_status': result.get('trade_status'),

 'trade_no': result.get('trade_no'),

 'buyer_id': result.get('buyer_user_id'),

 'total_amount': result.get('total_amount')

            }

 else:

            logger.warning(f"查询返回非成功状态: {result}")

 return {

 'success': False,

 'error_code': result.get('code'),

 'error_msg': result.get('msg', '未知错误')

            }

 except  Exception  as e:

 import traceback

        logger.error(f"查询订单 {order_sn} 状态异常: {e}")

        logger.error(traceback.format_exc())

 return {

 'success': False,

 'error_msg': str(e)

        }

# 更新订单状态

def  update_order_status(db_conn, order_id, trade_no, pay_time):

 try:

cursor = db_conn.cursor()

query =  """

            UPDATE fa_shop_order

            SET paystate = 1,

transactionid = %s,

paytime = %s,

updatetime = %s

WHERE id = %s

        """

current_time =  int(time.time())

        cursor.execute(query, (trade_no, pay_time, current_time, order_id))

        db_conn.commit()

affected_rows = cursor.rowcount

        cursor.close()

 return affected_rows >  0

 except  Exception  as e:

        logger.error(f"更新订单 {order_id} 状态失败: {e}")

        db_conn.rollback()

 return  False

# 主函数

def  main():

    logger.info("开始执行订单支付状态检查脚本")

 try:

 # 创建数据库连接

 try:

            logger.info("尝试连接数据库...")

db_conn = mysql.connector.connect(**DB_CONFIG)

            logger.info("数据库连接成功")

 except mysql.connector.Error as err:

            logger.error(f"数据库连接失败: {err}")

 import traceback

            logger.error(f"错误详情: {traceback.format_exc()}")

 return

 # 初始化支付宝客户端

alipay_client = init_alipay_client()

 # 获取未支付订单

unpaid_orders = get_unpaid_orders(db_conn)

order_count =  len(unpaid_orders)

        logger.info(f"获取到 {order_count} 个未支付订单")

 # 如果没有订单需要处理,则结束脚本

 if order_count ==  0:

            logger.info("没有需要处理的订单,脚本结束")

            db_conn.close()

 return

orders_processed =  0

orders_updated =  0

 # 逐个处理订单

 for order in unpaid_orders:

orders_processed +=  1

order_sn = order['order_sn']

            logger.info(f"检查订单 {order_sn} ({orders_processed}/{order_count})")

 # 避免API调用过于频繁

            time.sleep(1)  # 增加到1秒,减少API调用频率

 # 查询支付宝订单状态

query_result = query_alipay_order_status(order_sn, alipay_client)

 if query_result['success']:

                logger.info(f"订单 {order_sn} 支付状态: {query_result['trade_status']}")

 # 判断交易是否成功

 if query_result['trade_status'] in ['TRADE_SUCCESS', 'TRADE_FINISHED']:

 # 更新订单状态为已支付

pay_time =  int(time.time())

updated = update_order_status(db_conn, order['id'], query_result['trade_no'], pay_time)

 if updated:

orders_updated +=  1

                        logger.info(f"订单 {order_sn} 状态已更新为已支付")

 else:

                        logger.warning(f"订单 {order_sn} 状态更新失败")

 else:

                logger.warning(f"查询订单 {order_sn} 失败: {query_result.get('error_msg', '未知错误')}")

        logger.info(f"脚本执行完毕,共处理 {orders_processed} 个订单,更新 {orders_updated} 个订单状态")

 except  Exception  as e:

        logger.error(f"脚本执行过程中发生错误: {e}")

 import traceback

        logger.error(f"错误详情: {traceback.format_exc()}")

 finally:

 # 关闭数据库连接

 if  'db_conn'  in  locals() and db_conn.is_connected():

            db_conn.close()

            logger.info("数据库连接已关闭")

    logger.info("脚本执行完成")

if  __name__  ==  "__main__":

    main()

这里py执行结果,也是一直报错。

于是我就想着,再看看pip包的说明吧。

结果我就发现了这么一句话。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

重大突破: 支付宝windows生成签名工具可能bug,用openssl好用点。

1.采用openssl,需要进入linux环境生成。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

[root@wSsTBB1016841 ~]# mkdir opensslalipay
[root@wSsTBB1016841 ~]# cd opensslalipay/
[root@wSsTBB1016841 opensslalipay]# ls
[root@wSsTBB1016841 opensslalipay]# genrsa -out app_private_key.pem
-bash: genrsa: command not found
[root@wSsTBB1016841 opensslalipay]# openssl
OpenSSL> genrsa -out app_private_key.pem
Generating RSA private key, 2048 bit long modulus
........................................................+++
.........+++
e is 65537 (0x10001)
OpenSSL> pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem
writing RSA key
OpenSSL> exit
[root@wSsTBB1016841 opensslalipay]# ls
app_private_key.pem  app_private_key_pkcs8.pem  app_public_key.pem

得到这三个证书

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

但是easypay,要的是cert格式的。那就需要转换。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

2. 证书转换pem转crt

基础概念

  • PEM (Privacy Enhanced Mail): 这是一种基于Base64编码的文件格式,通常用于存储加密相关的文件,如私钥、公钥和证书。PEM文件以.pem为扩展名,内容通常以-----BEGIN CERTIFICATE----------END CERTIFICATE-----包裹。
  • CRT (Certificate): 这通常是PEM格式的一个别名,特别是在某些操作系统或软件中,CRT文件实际上是PEM格式的证书文件,只是扩展名不同。

转换方法

通常,PEM到CRT的转换并不需要改变文件内容,只是重命名文件扩展名即可。例如,如果你有一个名为certificate.pem的文件,你可以简单地将其重命名为certificate.crt

转换结果(手动复制改后缀)如下图
[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

传上去。

3. 测试支付回调。成功了。。。

[fastadmin] 第三十九篇 FastAdmin easypay 支付宝支付 回调 验签名错误 bug解决

总结:这种涉及到windows工具的,都不可靠。 证书生成还是得用linux。这一直是php好大好大的坑啊。

本作品采用《CC 协议》,转载必须注明作者和本文链接
嗨,我是波波。曾经创业,有收获也有损失。我积累了丰富教学与编程经验,期待和你互动和进步! 公众号:上海PHP自学中心
wangchunbo
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
司机 @ 某医疗行业
文章
301
粉丝
346
喜欢
563
收藏
1124
排名:61
访问:12.5 万
私信
所有博文
社区赞助商