stripe-cli icon indicating copy to clipboard operation
stripe-cli copied to clipboard

Is it possible to emulate the whole payment process through the cli ?

Open Annakan opened this issue 3 years ago β€’ 10 comments

Feedback

I am trying to understand the flow of the API(s) and generate testing data for automated offline testing.

I am trying to follow through the cli the process here https://stripe.com/docs/payments/save-and-reuse but when I reach the 3 point that is suppose to happen client side, I can't seem to be able to emulate it through the cli.

I tried to add a payment method (card ) to the SetupIntent but it was refused by the API ( Some of the parameters you provided (payment_method) cannot be used when modifying a SetupIntent that was created by Checkout ...)

Is it possible ? Or should I somehow spin up a server just for that client step and it has to always be done manually with human intervention. I understand this constraint in production but in tests and to explore the API is is problematic.

I saw that the trigger command is able to emulate "all previous steps" before the requested triggered event but that is not the same since I can't follow the logic and try out various scenarii.

> stripe customers create 
{
  "id": "cus_ICuyPHVQA963Ot",
....
} 
> stripe checkout sessions create --success-url=http://localhost --cancel-url=http://localhost -d "payment_method_types[]=card" --mode="setup"
{
  "id": "cs_test_xbid6kRJOopHl3ikaa1bCmtKOPR3nbwU7IPK4EkJnZXO4kmokcuaEhnK",
[...]
  "mode": "setup",
  "payment_intent": null,
  "payment_method_types": [
    "card"
  ],
  "payment_status": "no_payment_required",
  "setup_intent": "seti_1HcUxFG3Y3TcgsjWXfLyzok1",
  "shipping": null,
  "shipping_address_collection": null,
  "submit_type": null,
  "subscription": null,
  "success_url": "http://localhost",
  "total_details": null
}

And then the customer should enter a card but how do I do that in the cli ?

Because of course when I try to get back the SetupIntent it has, of course, no payment_method attached so I can't continue:

> stripe  setup_intents retrieve seti_1HcUxFG3Y3TcgsjWXfLyzok1

{
  "id": "seti_1HcUxFG3Y3TcgsjWXfLyzok1",
  "object": "setup_intent",
[...]
  "livemode": false,
  "mandate": null,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": null,
  "payment_method_options": {
    "card": {
      "request_three_d_secure": "automatic"
    }
  },
  "payment_method_types": [
    "card"
  ],
[...]
}

Annakan avatar Oct 15 '20 18:10 Annakan

Hey @Annakan thanks for the careful detail. Can you explain what your end goal is here?

This flow you're describing may not be possible to perform programmatically for the reason you alluded to: you can't fully control the SetupIntents or PaymentIntents created automatically for use by Checkout the same way you normally would. This is by design. But you also shouldn't need to control these, as that is all managed on the Stripe-hosted Checkout session.

Are you trying to detect completion or state change? If so, I'd recommend looking at trigger (docs) to generate events you can detect with your webhook handler.

If you can explain more precisely what your end goal is, we may be able to offer other suggestions.

Hope that helps!

brendanm-stripe avatar Oct 19 '20 17:10 brendanm-stripe

Closing, but please update with some additional details if that doesn't meet your needs.

brendanm-stripe avatar Oct 21 '20 13:10 brendanm-stripe

Sorry for the delay in answering.

The goal was simply to follow through the cli the whole process to get a hang of the APIs manually and examine the various token, but also to collect manually, at first, and later on potentially to script the collection of the various token exchanged to set up unit tests and integration tests without external dependencies.

I am old school in expecting unit test to work fully locally ;) (And I suspect many development shops expect the same ;) )

I have seen that test-triggers exists but they seem to require a listening web server, an external route toward it ,etc .. all things that might not be present when unit testing or when trying to get a feel of the API.

I also saw that triggers "initiate" the steps before them with value I don't seem to control(price products etc ..). It is fine for manual tests i suppose, and I saw the "resent" option that might be used to resent events about tokens my tests have created but this is not exactly reproducible unit tests.

Of course I may have missed something and Stripe api and tools are still way above all bank tools I have experienced before. Thanks a lot for answering.

Annakan avatar Oct 21 '20 14:10 Annakan

OK I think I understand. The CLI actually interacts with the Stripe API, though, so it's important to be aware of that context. And it can't really help with much when completely offline. For this kind of testing you'd want to capture and replay events or objects at various stages. We don't recommend including any Stripe UI internals in your automated tests, preferring instead to mock those portions as a black box and using only the results like our test cards / payment methods (instead of generating tokens).

You can listen to webhooks locally using the CLI with stripe listen --forward-to=http://localhost/webhook-endpoint but again this is real events coming from the Stripe API in the test mode environment.

brendanm-stripe avatar Oct 21 '20 16:10 brendanm-stripe

Hi @brendanm-stripe, I have a similar use case, and I'm wondering how to pay/approve a checkout session programmatically.

My use case is that my front end uses Stripe Checkout, and then upon success the user is redirected back to my app with path /success?id={CHECKOUT_SESSION_ID} (which gets interpolated by Stripe with the actual checkout session ID), and then my front end sends the checkout session ID to my backend/API. Before saving/updating data, my backend fetches checkout session info to make sure it was paid and that the metadata checks out for this particular user/instance.

I'm writing functional/end-to-end tests for my backend right now, and so I'm looking to programmatically "move" a checkout from unpaid to paid so that I can then test that the endpoint that handles a successful checkout session.

I tried confirming the payment intent of a checkout session, but I get error "You cannot confirm PaymentIntents created by Checkout."

Any ideas on how to do this? I do understand that the Checkout UX is 100% Stripe-hosted, but it also seems like programmatic testing would be useful.

kweiberth avatar Feb 21 '21 17:02 kweiberth

Hi @brendanm-stripe do you happen to have an update on this? Is this something Stripe would consider building?

kweiberth avatar Apr 26 '21 21:04 kweiberth

I'm successfully testing whole payment process with end-to-end testing using Puppeteer. I believe unit tests should be offline so you could capture the webhook requests and resend them to your endpoint while performing unit tests. If you want an example for the e2e tests I'm using, I can share them.

DenizUgur avatar Aug 29 '21 11:08 DenizUgur

@kweiberth @DenizUgur Again i'd reiterate that it's not recommended to test parts of the flows involving Stripe UI.

We don't recommend including any Stripe UI internals in your automated tests, preferring instead to mock those portions as a black box and using only the results like our test cards / payment methods (instead of generating tokens).

@kweiberth Instead you could send a dummy event to your "test" endpoint with minimal data to complete the fulfillment. If you make a request to Stripe to retrieve the session, it won't be complete/paid, of course. Manipulating a session like this is not supported.

brendanm-stripe avatar Aug 30 '21 01:08 brendanm-stripe

I would test the Stripe side as well to be on the safe side. If it was possible to mock the portion between creating checkout session and stripe sending events to my webhook via stripe-cli then of course there isn't any need to test Stripe UI.

However, mocking the events you send from Stripe would mean that Stripe won't ever change anything on those events. Like for example the change on Sep 1 involving customer.subscription.update. Using the Stripe UI would test its effects on our system.

Until any updates from Stripe, I believe this is the most foolproof way of testing Stripe.

DenizUgur avatar Aug 30 '21 09:08 DenizUgur

@DenizUgur I too am using Puppeteer, and I agree with you that it is most foolproof to run integration/end-to-end tests by directly interacting with Stripe.

@brendanm-stripe I agree that we shouldn't be testing Stripe's UI, but it's the only way to complete a transaction if we're using Stripe Checkout. And in our case, we do fetch transaction details to assert that everything matches, so to your point:

If you make a request to Stripe to retrieve the session, it won't be complete/paid, of course.

This would cause our test to fail. So we'd have to mock that response. And then it's not as robust of an end-to-end test. It would be great if we could programmatically complete a Stripe Checkout session. I'm sure we're not the only two devs spinning up Puppeteer just to complete the session πŸ˜…

kweiberth avatar Aug 30 '21 11:08 kweiberth

I'm gonna be doing the same thing and not gonna like any second of it!

We should be able to confirm the Stripe Checkout from our backends. Potentially only available in dev-mode is fine by me.

JoachimKoenigslieb avatar Mar 10 '23 15:03 JoachimKoenigslieb

The CLI supports simulating completed Checkout payment sessions using stripe trigger checkout.session.completed.

If you want to simulate completed mode=setup sessions, you can do so using a custom fixture. Following the example of the flow above, we can exercise the setup flow as follows:

{
  "_meta": {
    "template_version": 0
  },
  "fixtures": [
    {
      "name": "checkout_session",
      "path": "/v1/checkout/sessions",
      "method": "post",
      "params": {
        "success_url": "https://httpbin.org/post",
        "cancel_url": "https://httpbin.org/post",
        "mode": "setup",
        "payment_method_types": [
          "card"
        ]
      }
    },
    {
      "name": "payment_page",
      "path": "/v1/payment_pages/${checkout_session:id}",
      "method": "get"
    },
    {
      "name": "payment_method",
      "path": "/v1/payment_methods",
      "method": "post",
      "params": {
        "type": "card",
        "card": {
          "token": "tok_visa"
        },
        "billing_details": {
          "email": "[email protected]"
        }
      }
    },
    {
      "name": "payment_page_confirm",
      "path": "/v1/payment_pages/${checkout_session:id}/confirm",
      "method": "post",
      "params": {
        "payment_method": "${payment_method:id}"
      }
    }
  ]
}

Then run the test using stripe fixtures ~/path/to/checkout-setup.session.completed.json

brendanm-stripe avatar Mar 10 '23 18:03 brendanm-stripe

Well.... Sorry for the snarky attitude.

That worked beautifully!!

JoachimKoenigslieb avatar Mar 13 '23 14:03 JoachimKoenigslieb

Thanks for confirming @JoachimKoenigslieb !

With the example fixture usage to complete a setup mode Checkout session (which can be customized as needed for each use case), I believe this issue can be closed.

brendanm-stripe avatar Mar 13 '23 16:03 brendanm-stripe

Many apologies for resurrecting this thread - I am experiencing this as well. My unit tests are using Stripe Mock which I run in a docker image and via github actions; so far this has worked fantastic!

I am now attempting to write a unit test for after the hosted checkout session has been completed and obviously when the session is pulled it's coming back as unpaid.

I'm assuming (?) the solution for this, if existing, is related to stripe trigger checkout.session.completed but I can't for the life figure out how that interacts with the stripe mock image not to mention how I'd call that from unit tests.

This feels like a pretty standard ask so I'm hoping I'm missing something sort of obvious..

Appreciate any advice

coladarci avatar Apr 15 '23 16:04 coladarci

I have a similar issue now. We have built API which creates checkout session and we want to confirm this session with CLI and fixtures.

I composed fixture with session ID from API response (cs_test_a1RKxxxxx) and payment method ID from the previous session like below.

{
  "_meta": {
    "template_version": 0
  },
  "fixtures": [
    {
      "name": "payment_page_confirm",
      "path": "/v1/payment_pages/cs_test_a1RKxxxxx/confirm",
      "method": "post",
      "params": {
        "payment_method": "pm_1N.....",
        "expected_amount": 6078
      }
    }
  ]
}

But I received error response

Setting up fixture for: payment_page_confirm
Running fixture for: payment_page_confirm
Request failed, status=400, body={
  "error": {
    "message": "An error has occurred confirming the Checkout Session.",
    "request_log_url": "https://dashboard.stripe.com/test/logs/req_0OkdGiZS3qzCRR?t=1691833739",
    "type": "invalid_request_error"
  }
}

Am I missing something or this is simply not supported usage of CLI?

seijik42 avatar Aug 12 '23 10:08 seijik42

Hi @seijik42 -san, Would you check the error log page on the dashboard through the error.request_log_url ? You can possibly check the detail of the error like the following screenshot.

γ‚Ήγ‚―γƒͺγƒΌγƒ³γ‚·γƒ§γƒƒγƒˆ 2023-09-11 14 11 31

Thanks.

hideokamoto-stripe avatar Sep 11 '23 05:09 hideokamoto-stripe

hi @DenizUgur, can you share a snippet of the Puppeteer code your using?

I was trying to run some integ tests with the Stripe CLI but ran into some issues logging in without user interaction...In my case I am running a transaction, and see if the purchase is recorded correctly in my purchase database. .Wondering if it's worth the setup.

Thank you

Cherchercher avatar Mar 27 '24 15:03 Cherchercher

Hi @Cherchercher,

It's been three years since I last did this, and my memory is a bit foggy. However, in a nutshell, I instructed Puppeteer to follow the same steps as a user for subscribing to our service. After that, if subsequent tests required the subscription to be paid, I simply awaited our database to be updated by Stripe.

For your reference, this was the subscription part. Previously, I was redirected to the checkout page.

This snippet is three years old

it("gets success page after entering subscription information at Stripe.com", async () => {
	await page.waitForSelector("#email");

	await page.click("#email", { delay: 100 });
	await page.keyboard.press("Backspace");
	await page.type("#email", "[email protected]", { delay: 50 });

	await page.click("#cardNumber", { delay: 100 });
	await page.keyboard.press("Backspace");
	await page.type("#cardNumber", "4242424242424242", {
		delay: 50,
	});

	await page.click("#cardExpiry", { delay: 100 });
	await page.keyboard.press("Backspace");
	await page.type("#cardExpiry", "10/26", { delay: 50 });

	await page.click("#cardCvc", { delay: 100 });
	await page.keyboard.press("Backspace");
	await page.type("#cardCvc", "424", { delay: 50 });

	await page.click("#billingName", { delay: 100 });
	await page.keyboard.press("Backspace");
	await page.type("#billingName", "John Doe", { delay: 50 });

	await page.select("#billingCountry", "US");

	await page.evaluate(() => {
		const submitButton = document.querySelector(
			"button.SubmitButton"
		);
		submitButton.click();
	});

	await page.waitForTimeout(10000);

	await page.waitForSelector(".wrapper h1");
	const text = await page.$eval(".wrapper h1", (e) => e.textContent);
	expect(text).toContain("Thank you!");
});

DenizUgur avatar Mar 28 '24 00:03 DenizUgur