服务端 Webhook

Python Webhook

对于使用 Python 的 Opencard 应用, 开发者可以参考以下 Python 示例代码,来实现 Webhook 服务。

  1. #!/usr/bin/env python
    # encoding: utf-8

    import BaseHTTPServer
    import time

    # jwcrypto 不是标准库,需额外安装: pip install jwcrypto
    from jwcrypto import jwk, jwe
    from jwcrypto.common import json_encode, json_decode

    # 配置项,测试时请修改以下配置
    HOST_NAME = 'localhost'
    PORT_NUMBER = 8000
    PSK_TABLE = {'0':jwk.JWK.from_json('{"kty":"oct","k":"MDEyMzQ1Njc4OWFiY2RlZg"}')}

    # Webhook 基本协议实现
    #
    # 以下代码为示例,未妥善处理业务字段或异常等因素
    class Webhook(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
    self.send_response(404)

    def do_POST(self):
    content_length = int(self.headers['Content-Length'])
    req_body = self.rfile.read(content_length)
    jwetoken = jwe.JWE()
    # 从 protected 字段中解出来 kid
    jwetoken.deserialize(req_body)
    kid = jwetoken.jose_header["kid"]
    # 通过 kid 确定要用哪个解密 key
    key = PSK_TABLE[kid]
    # 可以将 rid 打印到日志中,以便问题追查时 join 日志
    print time.asctime(), "Req head %s" % json_encode(jwetoken.jose_header)
    # 用 key 解密取得解密后的内容
    jwetoken.decrypt(key)
    print time.asctime(), "Req json %s" % jwetoken.payload.decode("unicode_escape")
    req = json_decode(jwetoken.payload)
    # 如果资源方有多张卡片,从 req 中解析出来 srcid,处理相应的业务逻辑
    if req["srcid"] == "123":
    # do something
    res = {"status":0,"msg":""}
    res["data"] = {
    "item_list":[{"title":"故宫博物院"}],
    "jump_url":"/path/to/page3"
    }
    else:
    res = {"status":2,"msg":"Invalid srcid"}
    # 处理业务响应结果
    payload = json_encode(res)
    print time.asctime(), "Res json %s" % payload.decode("unicode_escape")
    # 用请求的 header 和 key 生成加密结果,保持加密算法和头内容(kid/rid)与请求一致
    jwetoken = jwe.JWE(plaintext=payload,
    protected=json_encode(jwetoken.jose_header),
    recipient=key)
    res_body = jwetoken.serialize(compact=True)
    self.send_response(200)
    self.end_headers()
    self.wfile.write(res_body)

    if __name__ == '__main__':
    httpd = BaseHTTPServer.HTTPServer((HOST_NAME, PORT_NUMBER), Webhook)
    print time.asctime(), "Webhook Starts - http://%s:%s" % (HOST_NAME, PORT_NUMBER)
    try:
    httpd.serve_forever()
    except KeyboardInterrupt:
    pass
    httpd.server_close()
    print time.asctime(), "Webhook Stops - http://%s:%s" % (HOST_NAME, PORT_NUMBER)