Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
Add support for token as payment method
Browse files Browse the repository at this point in the history
  • Loading branch information
calm-mlin committed Aug 2, 2024
1 parent 8a0b61c commit 521ad95
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 45 deletions.
127 changes: 82 additions & 45 deletions localstripe/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ def __init__(self, source=None, customer=None, **kwargs):
super().__init__()

self._card_number = number
self.token = None

self.type = 'card'
self.metadata = {}
Expand Down Expand Up @@ -2205,19 +2206,26 @@ def __init__(self, id=None, customer=None, type=None,
assert type in ('card', 'sepa_debit')
assert billing_details is None or _type(billing_details) is dict
if type == 'card':
assert _type(card) is dict and card.keys() == {
'number', 'exp_month', 'exp_year', 'cvc'}
card['exp_month'] = try_convert_to_int(card['exp_month'])
card['exp_year'] = try_convert_to_int(card['exp_year'])
assert _type(card['number']) is str
assert _type(card['exp_month']) is int
assert _type(card['exp_year']) is int
assert _type(card['cvc']) is str
assert len(card['number']) in (15, 16)
assert card['exp_month'] >= 1 and card['exp_month'] <= 12
if card['exp_year'] > 0 and card['exp_year'] < 100:
card['exp_year'] += 2000
assert len(card['cvc']) in (3, 4)
assert _type(card) is dict and (
card.keys() == {
'number', 'exp_month', 'exp_year', 'cvc'}
or card.keys() == {'token'})

if 'token' in card:
assert (_type(card['token']) is str
and card['token'].startswith('tok_'))
else:
card['exp_month'] = try_convert_to_int(card['exp_month'])
card['exp_year'] = try_convert_to_int(card['exp_year'])
assert _type(card['number']) is str
assert _type(card['exp_month']) is int
assert _type(card['exp_year']) is int
assert _type(card['cvc']) is str
assert len(card['number']) in (15, 16)
assert card['exp_month'] >= 1 and card['exp_month'] <= 12
if card['exp_year'] > 0 and card['exp_year'] < 100:
card['exp_year'] += 2000
assert len(card['cvc']) in (3, 4)
elif type == 'sepa_debit':
assert _type(sepa_debit) is dict
assert 'iban' in sepa_debit
Expand All @@ -2226,7 +2234,7 @@ def __init__(self, id=None, customer=None, type=None,
except AssertionError:
raise UserError(400, 'Bad request')

if type == 'card':
if type == 'card' and 'token' not in card:
if not (2018 <= card['exp_year'] < 2100):
raise UserError(400, 'Bad request',
{'code': 'invalid_expiry_year'})
Expand All @@ -2240,17 +2248,22 @@ def __init__(self, id=None, customer=None, type=None,
self.metadata = metadata or {}

if self.type == 'card':
self._card_number = card['number']
self.card = {
'exp_month': card['exp_month'],
'exp_year': card['exp_year'],
'last4': self._card_number[-4:],
'brand': 'visa',
'country': 'FR',
'fingerprint': fingerprint(self._card_number),
'funding': 'credit',
'three_d_secure_usage': {'supported': True},
}
self._card_number = None
if 'token' in card:
self.token = card['token']
self.card = Token._api_retrieve(card['token']).card
else:
self._card_number = card['number']
self.card = {
'exp_month': card['exp_month'],
'exp_year': card['exp_year'],
'last4': self._card_number[-4:],
'brand': 'visa',
'country': 'FR',
'fingerprint': fingerprint(self._card_number),
'funding': 'credit',
'three_d_secure_usage': {'supported': True},
}
elif self.type == 'sepa_debit':
self._sepa_debit_iban = \
re.sub(r'\s', '', sepa_debit['iban']).upper()
Expand All @@ -2265,42 +2278,66 @@ def __init__(self, id=None, customer=None, type=None,

def _requires_authentication(self):
if self.type == 'card':
return self._card_number in ('4000002500003155',
'4000002760003184',
'4000008260003178',
'4000000000003220',
'4000000000003063',
'4000008400001629')
if self._card_number is not None:
return self._card_number in ('4000002500003155',
'4000002760003184',
'4000008260003178',
'4000000000003220',
'4000000000003063',
'4000008400001629')
if self.token is not None:
return self.token in (
'tok_visa_chargeDeclinedInsufficientFunds')
return False

def _attaching_is_declined(self):
if self.type == 'card':
return self._card_number in ('4000000000000002',
'4000000000009995',
'4000000000009987',
'4000000000009979',
'4000000000000069',
'4000000000000127',
'4000000000000119',
'4242424242424241')
if self._card_number is not None:
return self._card_number in ('4000000000000002',
'4000000000009995',
'4000000000009987',
'4000000000009979',
'4000000000000069',
'4000000000000127',
'4000000000000119',
'4242424242424241')
if self.token is not None:
return self.token in (
'tok_visa_chargeDeclined',
'tok_visa_chargeDeclinedLostCard',
'tok_visa_chargeDeclinedStolenCard',
'tok_chargeDeclinedExpiredCard',
'tok_chargeDeclinedIncorrectCvc',
'tok_chargeDeclinedProcessingError',
'tok_visa_chargeDeclinedVelocityLimitExceeded')
return False

def _charging_is_declined(self):
if self.type == 'card':
return self._card_number in ('4000000000000341',
'4000008260003178',
'4000008400001629')
if self._card_number is not None:
return self._card_number in ('4000000000000341',
'4000008260003178',
'4000008400001629')
if self.token is not None:
return self.token in (
'tok_chargeCustomerFail',
'tok_visa_chargeDeclinedInsufficientFunds')
elif self.type == 'sepa_debit':
return self._sepa_debit_iban == 'DE62370400440532013001'
return False

def _decline_code(self):
if self.type == 'card':
if self._card_number == '4000000000009995':
if (
self._card_number == '4000000000009995'
or self.token == 'tok_visa_chargeDeclinedInsufficientFunds'
):
return 'insufficient_funds'
if self._card_number == '4000000000009987':
if (self._card_number == '4000000000009987'
or self.token == 'tok_visa_chargeDeclinedLostCard'):
return 'lost_card'
if self._card_number == '4000000000009979':
if (self._card_number == '4000000000009979'
or self.token == "tok_visa_chargeDeclinedStolenCard"):
return 'stolen_card'
return None

Expand Down
6 changes: 6 additions & 0 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ cus=$(curl -sSfg -u $SK: $HOST/v1/customers \
-d email=john.malkovich@example.com \
| grep -oE 'cus_\w+' | head -n 1)

# test that we can create a payment method using a token
curl -sSfg -u $SK: $HOST/v1/payment_methods \
-d type=card \
-d card[token]='tok_visa' \
| grep -oE 'pm_\w+' | head -n 1

pm=$(curl -sSfg -u $SK: $HOST/v1/payment_methods \
-d type=card \
-d card[number]=4242424242424242 \
Expand Down

0 comments on commit 521ad95

Please sign in to comment.