Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stripe checkout session is missing metadata

I have been trying to pass metadata through stripe.checkout.Session.create() like so:

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)

and neither the responses from stripe nor the events passing through my webhook contains any metadata, even though the event logs in the stripe console for the request and the response both contain:

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

I am listening to all events using stripe listen --forward-to localhost:8000/hooks/ --print-json and all that the endpoint at /hooks does is print the event to stdout. nothing else.

I would like for this metadata to be passed through my series of booking validation webhooks. referencing this:

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

Basically i am following these docs, sending metadata through the call for checkout.Session.create(), and then not seeing this metadata. I have tried using the dict() constructor, using dict syntax instead ({"key":"val"}), creating a variable and setting it to this dict before passing it through the function, and every other way i could think of to pass this metadata dictionary in, but i have not been getting it back from stripe.

Here is the hook i have set up where these events are being forwarded:

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'])

And the output to stdout:

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': '[email protected]',
                      '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}

in all these events metadata is 'metadata': <StripeObject at 0x103d64a40> JSON: {}

like image 241
Ed Danileyko Avatar asked Jan 24 '26 07:01

Ed Danileyko


2 Answers

This question has been answered but since the answers weren't immediately clear to me until further experimentation, I'm just proposing a little extra information for anyone having similar issues.

In the Session creation snippet below, pay attention to the TWO metadata declarations:

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,
    },
  ]
)

To test Webhooks, I used the command stripe listen --forward-to localhost:/webhook via the Stripe CLI and obtained the following results from a successful payment transaction with my app. This is what I observed:

  • My 'FOO' data was available in the payload received from the webhook callback for checkout.session.completed only.

  • My 'BAR' data was available in the payload received from the webhook callback for payment_intent.created, payment_intent.succeeded, and charge.succeeded only.

  • Neither 'FOO' nor 'BAR' were available in customer.created or customer.updated.

like image 147
DangerPaws Avatar answered Jan 25 '26 21:01

DangerPaws


What exactly do you mean by "session response" here? Can you provide an example?

For the webhook, which exact event type are you subscribed to? If, for example, you're listening to payment_intent.succeeded instead of checkout.session.completed, then it would be expected for the session metadata to not be present. You can optionally provide metadata to the underlying payment intent using payment_intent_data[metadata][key]=val, which would then be included in the payment intent event bodies.

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

like image 31
Nolan H Avatar answered Jan 25 '26 20:01

Nolan H



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!