演示如下:

扫码

最近弄了一个树莓派,便想试试有没有什么好玩的项目,于是利用手头技能做了一个这个。

前提条件

  • 支付:

    支付宝下的当面付产品,个人可签约。提供用户扫码支付和商家扫用户付款码两种方式。此处我们选择商家扫用户付款码的方式。

    https://opendocs.alipay.com/open/54/104506

  • 扫码

    树莓派可连接摄像模块,来进行“扫码”操作。

  • 程序实现

    结合SDK类型与扫码操作,选择使用python3实现。

整体流程

  • 支付

    上面支付宝提供了当面付的demo,但是只有Java和php版本,但是提供了整的SDK的python版本(https://github.com/alipay/alipay-sdk-python-all),所以我们需要按照当面付的demo来用Python实现。

  • 扫码

    直接使用摄像头模块拍照后识别收款码中的条码或者二维码即可拿到当前用户的信息。

    将扫码获得的用户信息传入上面的支付流程中即可完成支付。

具体实现流程

github地址:https://github.com/yumusb/scan2pay,配合代码阅读本文更佳。

  • 扫码部分

    picamera(树莓派摄像头模块),PIL(图片文件流读取) ,pyzbar(解码,二维码、条形码)

    def scan():
        stream = BytesIO()
        camera = PiCamera()
        camera.start_preview()
        sleep(2)
        #camera.capture('new.jpg') 保存图片,利于查看效果
        camera.capture(stream, format='jpeg')
        # "Rewind" the stream to the beginning so we can read its content
        stream.seek(0)
        image = Image.open(stream)
        camera.close() # 此处一定要关闭,不然下次调用时候会提示占用
        try:
            decodedata=decode(image)
            if(len(decodedata)==0):
                return ""
                #未解码出数据,结果集长度为0
            userinfo=decodedata[0].data
            if(len(userinfo)==18):
                return userinfo
            else:
                print(f"{userinfo}不是正确的收款码")
                # userinfo 是18位数字,且以28开头。
                return ""
        except Exception as e:
            print(traceback.format_exc())
            return ""
    
  • 支付部分

    1. 新建config对象,设置私钥、公钥、appid。

    2. 新建交易对象,设置交易用户、金额、标题、商户订单号、交易类型

    3. 发起支付

    def pay(userinfo):
        """
        设置配置,包括支付宝网关地址、app_id、应用私钥、支付宝公钥等,其他配置值可以查看AlipayClientConfig的定义。
        """
        alipay_client_config = AlipayClientConfig()
        alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
        alipay_client_config.app_id = '' # appid
        alipay_client_config.app_private_key = '' # 用户私钥
        alipay_client_config.alipay_public_key = '' # 支付宝公钥(填入用户私钥,返回的支付宝公钥,而不是上面用户私钥对应的公钥,如果不会配置的话可以看我另一项目中的文档 https://github.com/yumusb/alipay-f2fpay/tree/master/%E5%AF%86%E9%92%A5%E5%AF%B9%E7%94%9F%E6%88%90%E4%B8%8E%E8%AE%BE%E7%BD%AE%E6%95%99%E7%A8%8B)
      
        """
        得到客户端对象。
        注意,一个alipay_client_config对象对应一个DefaultAlipayClient,定义DefaultAlipayClient对象后,alipay_client_config不得修改,如果想使用不同的配置,请定义不同的DefaultAlipayClient。
        logger参数用于打印日志,不传则不打印,建议传递。
        """
        #client = DefaultAlipayClient(alipay_client_config=alipay_client_config, logger=logger)
        client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
        """
        系统接口示例:alipay.trade.pay
        """
        # 对照接口文档,构造请求对象
        model = AlipayTradePayModel()
        model.auth_code = userinfo
        model.subject = "Test" # 订单标题
        model.out_trade_no = time() # 订单账号
        model.scene = "bar_code"
        model.total_amount = 0.01 # 订单金额
        request = AlipayTradePayRequest(biz_model=model)
        response_content = None
        try:
            response_content = client.execute(request)
        except Exception as e:
            print(traceback.format_exc())
        if not response_content:
            print("failed execute")
        else:
            response = AlipayTradePayResponse()
            # 解析响应结果
            response.parse_response_content(response_content)
            #print(response.body)
            #返回结果集,如果不能成功支付的话,建议输出以排错
            if response.is_success():
                # 如果业务成功,则通过respnse属性获取需要的值
                print("get response trade_no:" + response.trade_no)
            else:
                # 如果业务失败,则从错误码中可以得知错误情况,具体错误码信息可以查看接口文档
                print(response.code + "," + response.msg + "," + response.sub_code + "," + response.sub_msg)
    

更多内容请看github。