@@ -2203,6 +2203,10 @@ def _api_detach(cls, id, **kwargs):
2203
2203
2204
2204
@classmethod
2205
2205
def _api_retrieve (cls , id ):
2206
+ obj = cls ._try_get_canonical_test_article (id )
2207
+ if obj :
2208
+ return obj
2209
+
2206
2210
# https://stripe.com/docs/payments/payment-methods#transitioning
2207
2211
# You can retrieve all saved compatible payment instruments through the
2208
2212
# Payment Methods API.
@@ -2213,6 +2217,41 @@ def _api_retrieve(cls, id):
2213
2217
2214
2218
return super ()._api_retrieve (id )
2215
2219
2220
+ @classmethod
2221
+ def _try_get_canonical_test_article (cls , id ):
2222
+ """Convert special payment method IDs into payment method objects.
2223
+
2224
+ See https://docs.stripe.com/testing?testing-method=payment-methods.
2225
+
2226
+ Oddly, as we do here, Stripe will convert these special test IDs into
2227
+ actual objects and store them on a GET request, meaning the GET has
2228
+ side effects and is not idempotent."""
2229
+
2230
+ if id == 'pm_card_visa' :
2231
+ return PaymentMethod (
2232
+ type = 'card' ,
2233
+ card = dict (
2234
+ number = '4242424242424242' ,
2235
+ exp_month = '12' ,
2236
+ exp_year = '2030' ,
2237
+ cvc = '123' ))
2238
+ if id == 'pm_card_visa_chargeDeclined' :
2239
+ return PaymentMethod (
2240
+ type = 'card' ,
2241
+ card = dict (
2242
+ number = '4000000000000002' ,
2243
+ exp_month = '12' ,
2244
+ exp_year = '2030' ,
2245
+ cvc = '123' ))
2246
+ if id == 'pm_card_chargeCustomerFail' :
2247
+ return PaymentMethod (
2248
+ type = 'card' ,
2249
+ card = dict (
2250
+ number = '4000000000000341' ,
2251
+ exp_month = '12' ,
2252
+ exp_year = '2030' ,
2253
+ cvc = '123' ))
2254
+
2216
2255
@classmethod
2217
2256
def _api_list_all (cls , url , customer = None , type = None , limit = None ,
2218
2257
starting_after = None ):
@@ -2704,7 +2743,7 @@ def __init__(self, customer=None, usage=None, payment_method_types=None,
2704
2743
2705
2744
@classmethod
2706
2745
def _api_confirm (cls , id , use_stripe_sdk = None , client_secret = None ,
2707
- payment_method_data = None , ** kwargs ):
2746
+ payment_method = None , payment_method_data = None , ** kwargs ):
2708
2747
if kwargs :
2709
2748
raise UserError (400 , 'Unexpected ' + ', ' .join (kwargs .keys ()))
2710
2749
@@ -2722,27 +2761,13 @@ def _api_confirm(cls, id, use_stripe_sdk=None, client_secret=None,
2722
2761
if client_secret and client_secret != obj .client_secret :
2723
2762
raise UserError (401 , 'Unauthorized' )
2724
2763
2725
- if payment_method_data :
2726
- if obj .payment_method is not None :
2727
- raise UserError (400 , 'Bad request' )
2728
-
2764
+ if payment_method is not None :
2765
+ assert isinstance (payment_method , str )
2766
+ pm = PaymentMethod ._api_retrieve (payment_method )
2767
+ obj ._attach_pm (pm )
2768
+ elif payment_method_data is not None :
2729
2769
pm = PaymentMethod (** payment_method_data )
2730
- obj .payment_method = pm .id
2731
-
2732
- if pm ._attaching_is_declined ():
2733
- obj .status = 'canceled'
2734
- obj .next_action = None
2735
- raise UserError (402 , 'Your card was declined.' ,
2736
- {'code' : 'card_declined' })
2737
- elif pm ._requires_authentication ():
2738
- obj .status = 'requires_action'
2739
- obj .next_action = {'type' : 'use_stripe_sdk' ,
2740
- 'use_stripe_sdk' : {
2741
- 'type' : 'three_d_secure_redirect' ,
2742
- 'stripe_js' : '' }}
2743
- else :
2744
- obj .status = 'succeeded'
2745
- obj .next_action = None
2770
+ obj ._attach_pm (pm )
2746
2771
elif obj .payment_method is None :
2747
2772
obj .status = 'requires_payment_method'
2748
2773
obj .next_action = None
@@ -2751,6 +2776,25 @@ def _api_confirm(cls, id, use_stripe_sdk=None, client_secret=None,
2751
2776
obj .next_action = None
2752
2777
return obj
2753
2778
2779
+ def _attach_pm (self , pm ):
2780
+ self .payment_method = pm .id
2781
+ self .payment_method_types = [pm .type ]
2782
+
2783
+ if pm ._attaching_is_declined ():
2784
+ self .status = 'canceled'
2785
+ self .next_action = None
2786
+ raise UserError (402 , 'Your card was declined.' ,
2787
+ {'code' : 'card_declined' })
2788
+ elif pm ._requires_authentication ():
2789
+ self .status = 'requires_action'
2790
+ self .next_action = {'type' : 'use_stripe_sdk' ,
2791
+ 'use_stripe_sdk' : {
2792
+ 'type' : 'three_d_secure_redirect' ,
2793
+ 'stripe_js' : '' }}
2794
+ else :
2795
+ self .status = 'succeeded'
2796
+ self .next_action = None
2797
+
2754
2798
@classmethod
2755
2799
def _api_cancel (cls , id , use_stripe_sdk = None , client_secret = None ,
2756
2800
** kwargs ):
0 commit comments