Stripe 结帐会话缺少元数据

Stripe checkout session is missing metadata

我一直在尝试通过 stripe.checkout.Session.create() 传递元数据,如下所示:

stripe.api_key = STRIPE_SECRET_KEY

payments_blueprint = Blueprint('payments', __name__, url_prefix='/payments')


@payments_blueprint.route('/checkout', methods=['POST'])
def create_checkout_session():

    try:
        checkout_session = stripe.checkout.Session.create(
            metadata=dict(key='val'),
            payment_method_types=['card'],
            line_items=request.form.get("lineItems", LINE_ITEMS),
            success_url=f'{request.environ["HTTP_ORIGIN"]}/success',
            cancel_url=f'{request.environ["HTTP_ORIGIN"]}/cancel',
            mode='payment'
        )

        return redirect(checkout_session.url, code=HTTPStatus.SEE_OTHER)

    except stripe.error.InvalidRequestError as err:
        return redirect(f'{request.environ["HTTP_ORIGIN"]}/error', code=HTTPStatus.MOVED_PERMANENTLY)

来自 stripe 的响应和通过我的 webhook 的事件都不包含任何元数据,即使 stripe 控制台中的请求和响应事件日志都包含:

  "metadata": {
    "key": "val"
  },...

我正在使用 stripe listen --forward-to localhost:8000/hooks/ --print-json 监听所有事件,/hooks 端点所做的只是将事件打印到标准输出。没有别的。

我希望通过我的一系列预订验证 webhook 传递此元数据。参考这个:

https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-metadata

基本上我在关注这些文档,通过 checkout.Session.create() 的调用发送元数据,然后看不到这些元数据。我试过使用 dict() 构造函数,使用字典语法代替 ({"key":"val"}),创建一个变量并将其设置为这个字典,然后再通过函数传递它,以及我能想到的所有其他传递方式这个元数据字典,但我还没有从 stripe 取回它。

这是我设置的用于转发这些事件的挂钩:

class TestHook(Resource):

    def post(self):
        event = stripe.Event.construct_from(
            json.loads(request.data),
            stripe.api_key
        ).to_dict()

        print(event['type'])
        pprint(event['data']['object'])

并且输出到标准输出:

payment_intent.created
{'amount': 20000,
 'amount_capturable': 0,
 'amount_received': 0,
 'application': None,
 'application_fee_amount': None,
 'canceled_at': None,
 'cancellation_reason': None,
 'capture_method': 'automatic',
 'charges': {},
 'client_secret': 'pi_3JTYxxxx7t',
 'confirmation_method': 'automatic',
 'created': 1630184808,
 'currency': 'usd',
 'customer': None,
 'description': None,
 'id': 'pi_3JTYxxxxVm4',
 'invoice': None,
 'last_payment_error': None,
 'livemode': False,
 'metadata': <StripeObject at 0x105c061d0> JSON: {},
 'next_action': None,
 'object': 'payment_intent',
 'on_behalf_of': None,
 'payment_method': None,
 'payment_method_options': {'card': {'installments': None,
                                     'network': None,
                                     'request_three_d_secure': 'automatic'}},
 'payment_method_types': ['card'],
 'receipt_email': None,
 'review': None,
 'setup_future_usage': None,
 'shipping': None,
 'source': None,
 'statement_descriptor': None,
 'statement_descriptor_suffix': None,
 'status': 'requires_payment_method',
 'transfer_data': None,
 'transfer_group': None}

checkout.session.completed
{'allow_promotion_codes': None,
 'amount_subtotal': 20000,
 'amount_total': 20000,
 'automatic_tax': {'enabled': False,
                   'status': None},
 'billing_address_collection': None,
 'cancel_url': 'http://localhost:9000/#/guides/cozumel-buzos-del-caribe/trips/7-day-dive?cancelpayment=true',
 'client_reference_id': None,
 'currency': 'usd',
 'customer': 'cus_K7oxxxguu',
 'customer_details': {'email': 'abc@gmail.com',
                      'tax_exempt': 'none',
                      'tax_ids': []},
 'customer_email': None,
 'id': 'cs_test_b1Yxxx9dM',
 'livemode': False,
 'locale': None,
 'metadata': <StripeObject at 0x103d64a40> JSON: {},
 'mode': 'payment',
 'object': 'checkout.session',
 'payment_intent': 'pi_3JTYxxxVm4',
 'payment_method_options': <StripeObject at 0x103d648b0> JSON: {},
 'payment_method_types': ['card'],
 'payment_status': 'paid',
 'setup_intent': None,
 'shipping': None,
 'shipping_address_collection': None,
 'submit_type': None,
 'subscription': None,
 'success_url': 'http://localhost:9000/#/payment/success',
 'total_details': {'amount_discount': 0,
                   'amount_shipping': 0,
                   'amount_tax': 0},
 'url': None}

在所有这些事件中,元数据是 'metadata': <StripeObject at 0x103d64a40> JSON: {}

这里的“会话响应”到底是什么意思?你能举个例子吗?

对于 webhook,您订阅了哪个确切事件 type?例如,如果您正在收听 payment_intent.succeeded 而不是 checkout.session.completed,那么预计 session metadata 不存在.您可以选择使用 payment_intent_data[metadata][key]=val 向基础支付意向提供 metadata,然后将其包含在支付意向事件主体中。

https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-payment_intent_data-metadata

在与 stripe 的人们讨论他们的不和谐(他们非常有帮助)上花了一些时间后,我发现使用问题中描述的方法发布到结帐会话的元数据应该持续存在,但仅在结帐会话流程中。它不会出现在任何其他事件中(即 charge、paymentIntent 或其他流)。重构我的代码后,我设法在 checkout_session.complete 事件中看到了这个元数据,所以正如预期的那样......我做了一些愚蠢的事情。但需要注意的重要一点是,除了结帐会话之外,在任何后续事件中都不会看到此元数据,这是我起初不理解的事情。元数据没有出现在随后的 checkout session 事件中是由于椅子和键盘之间的问题,暂时我不会再问任何问题。

非常感谢所有试图回答并指导我条纹的人。这实际上以一种很好的方式打开了一堆蠕虫。

此问题已得到解答,但由于在进一步实验之前我无法立即弄清楚答案,因此我只是为遇到类似问题的任何人提供一些额外信息。

在下面的会话创建片段中,注意两个元数据声明:

stripe.checkout.Session.create(
  success_url=success_url,
  cancel_url=cancel_url,
  payment_method_types=["card"],
  mode="payment",
  metadata={
    "foo": "FOO",
  },
  payment_intent_data={
    "metadata": {
      "bar": "BAR",
    }
  },
  line_items=[
    {
      "name": product_name,
      "quantity": quantity,
      "currency": currency,
      "amount": unit_price,
    },
  ]
)

为了测试 Webhooks,我通过 Stripe CLI 使用了命令 stripe listen --forward-to localhost:/webhook,并从我的应用程序的成功支付交易中获得了以下结果。这是我观察到的:

  • 我的 'FOO' 数据在从 webhook 回调接收的负载中可用,仅适用于 checkout.session.completed

  • 我的 'BAR' 数据仅在 payment_intent.createdpayment_intent.succeededcharge.succeeded 的 Webhook 回调接收的有效负载中可用。

  • 'FOO' 和 'BAR' 在 customer.createdcustomer.updated 中均不可用。