python 支付宝营销活动现金红包开发接入流程-含接口调用加签

news2024/12/30 3:34:34

1 创建网页/移动应用

2 配置接口加签方式

在这里插入图片描述

涉及到金额的需要上传证书,在上传页面有教程,

在支付宝开放平台秘钥工具中生成CSR证书,会自动保存应用公钥和私钥到电脑上,调用支付宝接口需要应用私钥进行加签

在这里插入图片描述

在这里插入图片描述

上传完CSR证书后会有三个证书下载, 分别是: alipayRootCert.crt(支付宝根证书), alipayCertPublicKey_RSA2.crt(支付宝证书公钥), appCertPublicKey_网站应用id.crt(网站应用公钥),下载到本地,后续调用接口的时候加签要用到

3 绑定红包产品

在这里插入图片描述

发红包需要绑定红包、营销活动红包产品功能

配置完成后,对该网站应用进行上线申请,审核通过后就可以使用了

4 代码实现

现金红包接口文档: https://opendocs.alipay.com/open/029yy9

4.1 实现加签

可参考 https://blog.csdn.net/tm_tsm/article/details/105124809 文章,有点区别在 encode_for_sign 函数中,最后拼接才需要 urllib quote

import time
import binascii
import json
import urllib
import urllib.parse
from Cryptodome.Hash import SHA, SHA256
from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import PKCS1_v1_5

prv_key = "-----BEGIN RSA PRIVATE KEY-----\n" \
          "应用私钥" \
          "\n-----END RSA PRIVATE KEY-----"


class SignRSA:
    MAXLINESIZE = 76  # Excluding the CRLF
    MAXBINSIZE = (MAXLINESIZE // 4) * 3

    def __init__(self, **kwargs):
        self.kwargs = kwargs
        self.sign_type = "RSA2"  # rsa 用sha, rsa2方式用SHA256
        self.private_key = prv_key

    @staticmethod
    def get_ordered_data(data: dict):
        #  还没生成签名 前不能传 sign 和 sign_type 进行排序
        complex_keys = [k for k, v in data.items() if isinstance(v, dict)]

        # 将字典类型的数据dump出来
        for key in complex_keys:
            data[key] = json.dumps(data[key], separators=(',', ':'))
        return sorted([(k, v) for k, v in data.items()])

    @staticmethod
    def encode_for_sign(ordered_items, quote=False):
        ordered_items.sort()
        if quote:
            unsigned_str = "&".join('''{}={}'''.format(k, urllib.parse.quote(v)) for k, v in ordered_items)
        else:
            unsigned_str = "&".join('''{}={}'''.format(k, v) for k, v in ordered_items)
        return unsigned_str.encode('utf-8').decode('unicode_escape')

    def verify_with_public_key(self, sign):
        """
        :parameter sign:
            The signature that needs to be validated.
        :type sign: byte string
        """
        ordered_item = self.get_ordered_data(self.kwargs)
        params = "&".join(u"{}={}".format(k, v) for k, v in ordered_item)

        # 公钥验签
        signer = PKCS1_v1_5.new(RSA.importKey(self.public_key))

        if self.sign_type == 'RSA':
            msg_hash = SHA.new()
        else:
            msg_hash = SHA256.new()
        msg_hash.update(params.encode("utf8"))

        # sign = urllib.parse.unquote_plus(sign)
        # sign = self.decodebytes(sign.encode())  # 反操作:base64 编码,转换为unicode表示并移除回车
        return signer.verify(msg_hash, self.decodebytes(sign.encode("utf8")))  # true / false

    def sign_with_private_key(self):
        ordered_item = self.get_ordered_data(self.kwargs)
        unsigned_str = self.encode_for_sign(ordered_item)
        signer = PKCS1_v1_5.new(RSA.importKey(self.private_key))
        print("加签参数: ", unsigned_str)
        # rsa 用sha, rsa2方式用SHA256
        if self.sign_type == 'RSA':
            rand_hash = SHA.new()
        else:
            rand_hash = SHA256.new()
        rand_hash.update(unsigned_str.encode())
        signature = signer.sign(rand_hash)

        # base64 编码,转换为unicode表示并移除回车
        sign = self.encodebytes(signature).decode("utf8").replace("\n", "")

        data = self.kwargs
        data['sign'] = sign
        data['sign_type'] = self.sign_type
        ordered_data = self.get_ordered_data(data)
        print("加签结果:", sign)
        # 在最后拼接的时候才需要 urllib quote
        return f'''{self.encode_for_sign(ordered_data, quote=True)}'''

    def encodebytes(self, s):
        """Encode a bytestring into a bytes object containing multiple lines
        of base-64 data."""
        self._input_type_check(s)
        pieces = []
        for i in range(0, len(s), self.MAXBINSIZE):
            chunk = s[i: i + self.MAXBINSIZE]
            pieces.append(binascii.b2a_base64(chunk))
        return b"".join(pieces)

    def decodebytes(self, byte_str):
        """Decode a bytestring of base-64 data into a bytes object."""
        self._input_type_check(byte_str)
        return binascii.a2b_base64(byte_str)

    @staticmethod
    def _input_type_check(s):
        try:
            m = memoryview(s)
        except TypeError as err:
            msg = "expected bytes-like object, not %s" % s.__class__.__name__
            raise TypeError(msg) from err
        if m.format not in ('c', 'b', 'B'):
            msg = ("expected single byte elements, not %r from %s" %
                   (m.format, s.__class__.__name__))
            raise TypeError(msg)
        if m.ndim != 1:
            msg = ("expected 1-D data, not %d-D data from %s" %
                   (m.ndim, s.__class__.__name__))
            raise TypeError(msg)
4.2 支付宝根证书sn码、网站应用sn码获取

代码来源: https://blog.csdn.net/weixin_37309049/article/details/105319729

import OpenSSL
import hashlib
import re


def md5(string):
    return hashlib.md5(string.encode('utf-8')).hexdigest()


# 应用公钥证书序列号
def get_app_cert_cn(cert_str=None):
    cert_str = cert_str or open("appCertPublicKey_网站应用id.crt").read()
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_str)
    try:
        res = cert.get_signature_algorithm()
        # 根据其他语言算法 应该剔除不是sha加密的部分python2 可以用r'sha.+WithRSAEncryption' 但是python3必须是b'sha.+WithRSAEncryption'
        if not re.match(b'sha.+WithRSAEncryption', res):
            return None
    except:
        return None
    cert_issue = cert.get_issuer()
    op = ''
    b = list(cert_issue.get_components())
    # 证书签发机构排序方式应该是倒序的
    for i in range(len(b)):
        a = list(b[len(b) - 1 - i])
        # 在Python3中直接读取的a[0]为bytes,会影响加密结果,进行decode,兼容python2
        opp = "{}={}".format(a[0].decode(), a[1].decode())
        op = op + opp + ','
    return md5(op[:-1] + str(cert.get_serial_number()))


# 根证书序列号
def get_root_cn_sn():
    root_cert = open("alipayRootCert.crt").read()
    cert_list = root_cert.split('-----BEGIN CERTIFICATE-----')
    root_cert_sn = ''
    for i in cert_list:
        # print i, len(i)
        if not len(i):
            continue
        cert_sn = get_app_cert_cn('-----BEGIN CERTIFICATE-----' + i)
        if cert_sn is not None:
            root_cert_sn = root_cert_sn + cert_sn + '_'
    return root_cert_sn[:-1]


if __name__ == "__main__":
    print("根证书sn:", get_root_cn_sn())
    print("应用证书sn:", get_app_cert_cn())


4.3 创建现金红包
import time

import requests

# 导入上面加签的代码
import alipay_countersign

date_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

alipay_params = {
    "app_id": "网站应用id",
    "method": "alipay.marketing.campaign.cash.create",
    "charset": 'UTF-8',
    "sign_type": "RSA2",
    "timestamp": date_str,
    "version": "1.0",
    "alipay_root_cert_sn": "支付宝根证书sn码",
    "app_cert_sn": "网站应用sn码",
    "biz_content": {
        "coupon_name": "answer activity",   # 营销活动名称,暂时只能是英文,中文会报验签错误,应该是上面加签的代码还有点问题
        "prize_type": "fixed",
        "total_money": "10.00",
        "total_num": "50",
        "prize_msg": "answer activity",   # 红包详情页展示的文案
        "start_time": "NowTime",
        "end_time": "2023-03-10 12:00:00"
    }
}

sign_rsa = alipay_countersign.SignRSA(**alipay_params)
order_info = sign_rsa.sign_with_private_key()
url = "https://openapi.alipay.com/gateway.do?" + order_info

headers = {
    "content-type": "application/json"
}
data = requests.post(url, headers=headers)

print(data)
print(data.text)
4.4 红包发放
import time

import requests
# 加签代码
import alipay_countersign

date_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

alipay_params = {
    "app_id": "网站应用id",
    "method": "alipay.marketing.campaign.cash.trigger",
    "charset": 'UTF-8',
    "sign_type": "RSA2",
    "timestamp": date_str,
    "version": "1.0",
    "alipay_root_cert_sn": "支付宝根证书sn码",
    "app_cert_sn": "网站应用sn码",
    "biz_content": {
        "user_id": "用户id",
        "crowd_no": "创建现金红包活动的活动号",
    }
}
sign_rsa = alipay_countersign.SignRSA(**alipay_params)
order_info = sign_rsa.sign_with_private_key()

url = "https://openapi.alipay.com/gateway.do?" + order_info

headers = {
    "content-type": "application/json"
}
data = requests.post(url, headers=headers)
print(data)
print(data.text)

红包发放成功后在支付宝app–账单,或者红包–我的红包中可以看到红包流水,个人商家发的红包是没有支付消息通知的,需要在这两个地方才能看到流水详情

4.5 用户id获取
import time

import requests
# 加签
import alipay_countersign

date_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

alipay_params = {
    "app_id": "网站应用id",
    "method": "alipay.system.oauth.token",
    "charset": 'UTF-8',
    "sign_type": "RSA2",
    "timestamp": date_str,
    "version": "1.0",
    "grant_type": "authorization_code",
    "alipay_root_cert_sn": "支付宝根证书sn码",
    "app_cert_sn": "网站应用sn码",
    "code": "f0361800bf764a0c858dbc87671cVX20"   # h5获取到的auth_code
}

sign_rsa = alipay_countersign.SignRSA(**alipay_params)
order_info = sign_rsa.sign_with_private_key()
url = "https://openapi.alipay.com/gateway.do?" + order_info

headers = {
    "content-type": "application/json"
}
data = requests.post(url, headers=headers)
print(data)
print(data.text)
4.6 h5获取授权code
  <html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta
      name="viewport"
      content="width=750,user-scalable=yes,target-densitydpi=device-dpi"
    />
    <title></title>
    <link href="css/ttqhb.css" rel="stylesheet" type="text/css" />
    <script src="js/jquery.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body style="background: #c72211"></body>
  <a onclick="get_red_packet()"><img src="images/btn.png" class="btnimg" /></a>
  <img src="images/redbao.jpg" class="img100" />
  <script>
    function getQueryString(name) {
      var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
      var r = window.location.search.substr(1).match(reg);
      if (r != null) {
        return unescape(r[2]);
      }
      return null;
    }

    var url =
      "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=网站应用id&scope=auth_base&redirect_uri=";
    var red_uri = encodeURIComponent(
      "https://baidu.com//index.html" // 授权成功回跳地址
    );
    url += red_uri;
    var auth_code = getQueryString("auth_code");
    if (!auth_code) {
      window.location.href = url;
    }
  </script>
</html>

在支付宝上进入该页面会自动进行授权回调,授权成功后回调改url页面,在url参数中就会有授权auth_code

5 注意事项

需要是签约的商户才能使用转账功能,如果商户未签约需要进行签约

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/383963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

互联网衰退期,测试工程师35岁的路该怎么走...

国内的互联网行业发展较快&#xff0c;所以造成了技术研发类员工工作强度比较大&#xff0c;同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高&#xff0c;超过35岁的基层研发类员工&#xff0c;往往因为家庭原因、身体原因&#xff0c;比较难以跟得上工作…

DSP_TMS320F28335_PIE学习笔记

前言 本文重点探讨DSP PIE模块的学习笔记&#xff0c;由于学这部内容的时候&#xff0c;是用28335学的&#xff0c;所以标题是用的28335&#xff0c;但其实28377D和28335的PIE使用基本上是一样的&#xff0c;也是可以借鉴的。 正文 原理 讲点原理&#xff0c;PIE&#xff0…

linux minio更改密码MINIO_ACCESS_KEY报错

minio版本RELEASE.2020-11-13T20-10-18Z启动文件配置如下cat run.sh#!/bin/bashexport MINIO_ACCESS_KEYminioexport MINIO_SECRET_KEYfasffnohup /opt/minio/minio.RELEASE.2020-11-13T20-10-18Z server http://192.168.100.x/data/minio_data http://192.168.100.x/data/mini…

Docker中对已存在运行的容器修改端口映射

一、初次创建容器&#xff0c;指定宿主机和容器端口的映射&#xff0c;如下示例&#xff1a; docker run -itd -p 11935:1935 -p 11985:1985 -p 18080:8080 -p 20903:20903 -p 18888:8888 \ --restartalways \ -v /srv/srs3/conf/:/usr/local/srs/conf/ \ -v /srv/srs3/objs/:…

Android总结汇总

一、APP1、混合开发项目AHP地址&#xff1a;https://github.com/Witnin/AHP简介&#xff1a;KotlinJava二、路由框架1、集成ARouter导航框架官网地址&#xff08;停止维护&#xff09;&#xff1a;https://github.com/alibaba/ARouter/blob/master/README_CN.mdjadepeakpoet&am…

Leedcode 1011. 在 D 天内送达包裹的能力

题目 传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。 传送带上的第 i 个包裹的重量为 weights[i]。每一天&#xff0c;我们都会按给出重量&#xff08;weights&#xff09;的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。 返回能在 day…

利用DSCync进行域内权限维持

一个域环境可以拥有多台域控制器&#xff0c;每台域控制器各自存储着一份所在域的活动目录的可写副本&#xff0c;对目录的任何修改都可以从源域控制器同步到本域、域树或域林中的其他域控制器上。当一个域控想从另一个域控获取域数据更新时&#xff0c;客户端域控会向服务端域…

【Echarts图例点击事件】自定义Echarts图例legend点击事件(已解决)

目录先睹为快&#xff08;效果&#xff09;1、实现Echarts多条曲线2、点击echarts触发接口请求2.1 先默认隐藏部分数据2.2 自定义legend图例点击事件3、源码下载地址&#xff08;解压即用&#xff09;**【写在前面】**这下我又不得不说了&#xff0c;还是客户现场使用时想查询一…

宽度学习系统BLS推广到在线学习的论文阅读记录

BELS: A BROAD ENSEMBLE LEARNING SYSTEM FOR DATA STREAM CLASSIFICATION 摘要&#xff1a;这篇文章主要是将BLS推广到适用于在线学习的邻域&#xff0c;提出了其如何进行适合在线学习的增量更新&#xff0c;以及面对概念漂移的适应 所提出的算法BELS 稀疏特征映射的更新 …

【牛客网刷题记录】,后面遇到的一些问题都会在这里记录,欢迎大家批评指正

文章目录数据结构篇数组字符串链表树图堆算法篇哈希查找排序数据库篇SQL语言篇数据结构篇 数组 省流&#xff1a; 计算二维数组指定元素的位置压缩矩阵 1. 执行以下代码段(程序已包含所有必需的头文件)会输出什么结果。 char a[] "abcd", b[10] "abcd"…

被围绕的区域

题目链接 leetcode——在线链接 题目描述 给你一个 m x n 的矩阵 board &#xff0c;由若干字符 ‘X’ 和 ‘O’ &#xff0c;找到所有被 ‘X’ 围绕的区域&#xff0c;并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。 题目示例 输入&#xff1a;board [[“X”,“X”,“X”,…

德标1.4571合金元素钛的影响

1.45711.4571介绍&#xff1a;1.4571在316的基础上加入了钛&#xff0c;为解决316晶间腐蚀发展起来的钢种&#xff0c;有良好的耐晶间腐蚀性能&#xff0c;在稀H2SO4、H3PO4及有机酸和海洋大气中耐蚀性能有所提高。其它性能和316相近。◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆…

【项目实战】为什么我选择使用CloseableHttpClient,而不是HttpClient,他们俩有什么区别?

一、HttpClient介绍 HttpClient是Commons HttpClient的老版本&#xff0c;已被抛弃&#xff0c;不推荐使用&#xff1b; HttpClient是一个接口&#xff0c;定义了客户端HTTP协议的操作方法。 它可以用于发送HTTP请求和接收HTTP响应。 HttpClient接口提供了很多方法来定制请求…

RK3288-android8-IR-选不中小窗口

IR红外功能,多么基础的功能,但是说来也不简单 因为,小小的红外看似基础,实则设计太多东西了, 从关机涉及Uboot;到内核kernel键码上报;到android键码的实现,小小的功能涉及大大的范围; (101条消息) rk3288-android8-IR-mouse_旋风旋风的博客-CSDN博客 大家可以看一下我之前的…

考研复试——数据结构

文章目录数据结构什么是数据结构&#xff1f;逻辑结构和物理结构有什么区别&#xff1f;为什么对单链表设置头结点&#xff1f;算法的特点&#xff1f;常见的数据结构有哪些&#xff1f;栈在后缀表达式求值的算法思想&#xff1a;队列溢出现象&#xff1f;解决方法&#xff1f;…

【项目精选】基于Java的超级玛丽游戏的设计与实现(源码+论文+视频)

点击下载源码 超级玛丽小游戏的JAVA程序&#xff0c;进入游戏后首先按空格键开始&#xff0c;利用方向键来控制的马里奥的移动&#xff0c;同时检测马里奥与场景中的障碍物和敌人的碰撞&#xff0c;并判断马里奥的可移动性和马里奥的生命值。当马里奥通过最后一个场景后游戏结束…

项目经理如何管理好自己的时间

由于项目各种不定因素的存在&#xff0c;有时侯我们看到有些项目经理虽然成天忙得焦头烂额&#xff0c;但绩效甚微也就不足为怪了&#xff0c;如何能让宝贵的时间花在“刀刃”上&#xff0c;抓住了问题的要害&#xff0c;才能使工作事半功倍。 1、做好工作计划 项目经理比较擅…

目前已经有80多万个数据库迁移到了亚马逊云科技上

Gartner是全球最具权威的IT研究公司&#xff0c;在IT研究领域可以说是无人不知、无人不晓。它每年都会发布各种IT产业评测报告&#xff0c;分析未来技术发展&#xff0c;帮助客户进行市场分析、技术选择、投资决策。最近看到Gartner发布的云数据库魔力象限报告&#xff0c;这也…

安装包UI美化之路-nsNiuniuSkin安装包模板介绍

今天这篇文章&#xff0c;咱们不做功能讲解了&#xff0c;来介绍一下nsNiuniuSkin的几套安装包模板的UI效果和设计理念&#xff01; 前言 经常有朋友会问&#xff0c;nsNiuniuSkin的几套模板都有些什么区别呀&#xff0c;我拿到模板后&#xff0c;如果要换成我们自己的UI方便…

Java on VS Code 2月更新|JUnit 5 并行测试与 Spring Boot 插件的过滤功能

作者&#xff1a;Nick Zhu - Senior Program Manager, Developer Division at Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到我们的二月更新&#xff01;在此博客中&#xff0c;我们将为您带来与 JUnit 5 并行测试相关的新功能以及用于 Spring Boot Dashboa…