stripe / stripe-android

Stripe Android SDK
https://stripe.com/docs/mobile/android
MIT License
1.26k stars 640 forks source link

[BUG] Success payment after Alipay returns "Exception while parsing response body" error (webhook still succeeds) #5471

Closed fanwgwg closed 2 years ago

fanwgwg commented 2 years ago

Summary

I'm trying to integrate with Alipay using this SDK. However, after a successful alipay payment (I have alipay app installed on my device, the payment is redirected to the alipay app and it succeeds, I'm not able to reproduce this issue if Alipay is not installed), ApiResultCallback#onError was triggered, with the following error

Exception while parsing response body.
                                                                                                                          Status code: 200
                                                                                                                          Request-Id: null
                                                                                                                          Content-Type: text/html;charset=utf-8
                                                                                                                          Body: "<html>
                                                                                                      <head>
                                                                                                        <meta name="viewport" content="width=device-width, initial-scale=1">
                                                                                                        <link rel="stylesheet" type="text/css" href="/sources.css">
                                                                                                        <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
                                                                                                      </head>
                                                                                                      <body>
                                                                                                        <div id="container">
                                                                                                          <a href="stripe://return_url?payment_intent=pi_3Lb45HDemXDavOXa1vHXS4Jo&amp;payment_intent_client_secret=pi_3Lb45HDemXDavOXa1vHXS4Jo_secret_NZ4cxVzdsWnh5j7fEj0BwFJ3i&amp;redirect_status=succeeded" id="link">
                                                                                                            <div id="button">
                                                                                                              <svg width="12px" height="12px" id="arrow" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                                                                                                                <title>arrow--left--white</title>
                                                                                                                <defs></defs>
                                                                                                                <g id="0.4-Truncated" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                                                                                                                <g id="arrow--left--white" fill="#FFFFFF">
                                                                                                                <path d="M2.62346744,6.7068586 L6.50423068,10.6294609 C6.80672037,10.9352117 6.80763061,11.4462403 6.49636109,11.7608656 C6.18292147,12.0776845 5.68394732,12.0790523 5.37702405,11.7688201 L0.226232308,6.56249694 C0.0749573842,6.4095911 -0.00089268601,6.20534543 7.92628035e-06,6 C-0.00089268601,5.79465457 0.0749573842,5.5904089 0.226232308,5.43750306 L5.37702405,0.231179945 C5.68394732,-0.0790522993 6.18292147,-0.0776844911 6.49636109,0.239134372 C6.80763061,0.553759725 6.80672037,1.06478826 6.50423068,1.37053914 L2.8164057,5.09812305 L11.1939277,5.09812305 C11.6391091,5.09812305 12,5.45515361 12,5.90249082 C12,6.34673088 11.6522677,6.7068586 11.1939277,6.7068586 L2.62346744,6.7068586 L2.62346744,6.7068586 Z" id="Arrow"></path>
                                                                                                                </g>
                                                                                                                </g>
                                                                                                              </svg>
                                                                                                              Return to Merchant
                                                                                                            </div>
                                                                                                          </a>
                                                                                                        </div>
                                                                                                        <script src="/sources/redirect_complete.js"></script>
                                                                                                      </body>
                                                                                                    </html>
                                                                                                    "

Code to reproduce

  1. Make sure Alipay is installed. I'm only testing in PROD because the documentation says that Alipay is not supported in test mode.
  2. Trigger a Alipay payment follow this code https://stripe.com/docs/payments/alipay/accept-a-payment?platform=mobile&ui=android#android-redirect-alipay-wallet
  3. Complete the payment in Alipay app
  4. Notice that the ApiResultCallback#onError callback is triggered.

Android version

Android 12. Alipay sdk com.alipay.sdk:alipaysdk-android:15.8.11@aar from maven.

Impacted devices

Installation method

Through gradle implementation 'com.stripe:stripe-android:20.10.0'

Dependency Versions

kotlin: stripe-android: implementation 'com.stripe:stripe-android:20.10.0' Android Gradle Plugin: 7.3.0-rc01 Gradle: 7.4

SDK classes

Video

Other information

fanwgwg commented 2 years ago

Another thing I observe is that no matter Alipay is installed or not, if I cancel the payment within Alipay (e.g, tapping close button in the redirected Alipay activity), the PaymentIntentResult will contain status RequiresAction instead of Cancelled, with message We are unable to authenticate your payment method. Please choose a different payment method and try again

brnunes-stripe commented 2 years ago

Hi @fanwgwg, thanks for raising the issue. I'm working on reproducing and fixing this. If the issue is always reproducible, can you share the full stack trace for the exception?

brnunes-stripe commented 2 years ago

Hey @fanwgwg, I was able to reproduce and fixed the issue in #5554. Regarding the status after cancelation, RequiresAction is correct, and means that the user needs to confirm the payment on Alipay. Canceled in this case would mean that you've canceled the Payment Intent, and it cannot be confirmed anymore. See more about the Intent statuses on our docs: https://stripe.com/docs/payments/intents#intent-statuses. I also added examples of integrating with and without the Alipay SDK on the PR above. When the user cancels the flow, the Payment Intent stays in status RequiresAction, and you can try to confirm it again.

brnunes-stripe commented 2 years ago

The fix is live in v20.13.0, please try it out and let us know if you have any problems. The integration docs were also updated to clarify the RequiresAction status. Thanks again for raising this!

fanwgwg commented 2 years ago

Thank you @bruno-stripe for the quick action! I have one question regarding the PR #5554 and the documentation. In PR #5554, the example AlipayPaymentNativeActivity was updated with the following code block

StripeIntent.Status.RequiresAction ->
                            updateStatus("\n\nUser canceled confirmation")

From what I can understand, we're treating RequiresAction as cancellation here. However, at the documentation here, it still says that

Remember to handle the RequiresAction status and call handleNextActionForPayment to open the Alipay app (if installed) or shows its own UI and communicates the result back to the Stripe SDK automatically.

Should we still handle the RequiresAction by calling handleNextActionForPayment ?

fanwgwg commented 1 year ago

Friendly ping :)

brnunes-stripe commented 1 year ago

Hey @fanwgwg, thanks for pointing that out. I'll update the docs. That's not required, as the confirmAlipayPayment method will already handle authentication, and if the status is still RequiresAction after that, it means the user canceled the authentication.

  stripe.confirmAlipayPayment(
      ConfirmPaymentIntentParams.createAlipay(paymentIntentClientSecret),
      { data -> PayTask(this@AlipayActivity).payV2(data, true) }
  )