Skip to content

Commit

Permalink
Issue 30: Add functionality for updating credit card with masked number
Browse files Browse the repository at this point in the history
  • Loading branch information
vcatalano committed Feb 7, 2015
1 parent 98943a5 commit 5107dd8
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 2 deletions.
9 changes: 8 additions & 1 deletion authorize/apis/credit_card_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from authorize.apis.payment_profile_api import PaymentProfileAPI
from authorize.schemas import CreateCreditCardSchema
from authorize.schemas import UpdateCreditCardSchema
from authorize.schemas import ValidateCreditCardSchema
from authorize.xml_data import *

Expand All @@ -11,7 +12,7 @@ def create(self, customer_id, params={}):
return self.api._make_call(self._create_request(customer_id, card))

def update(self, customer_id, payment_id, params={}):
card = self._deserialize(CreateCreditCardSchema(), params)
card = self._deserialize(UpdateCreditCardSchema(), params)
return self.api._make_call(self._update_request(customer_id, payment_id, card))

def validate(self, customer_id, payment_id, params={}):
Expand All @@ -24,6 +25,12 @@ def _create_request(self, customer_id, card={}):
return self._make_xml('createCustomerPaymentProfileRequest', customer_id, None, params=card)

def _update_request(self, customer_id, payment_id, card={}):

# Issue 30: If only the last 4 digits of a credit card are provided,
# add the additional XXXX character mask that is required.
if len(card['card_number']) == 4:
card['card_number'] = 'XXXX' + card['card_number']

return self._make_xml('updateCustomerPaymentProfileRequest', customer_id, payment_id, params=card)

def _validate_request(self, customer_id, payment_id, card={}):
Expand Down
6 changes: 6 additions & 0 deletions authorize/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ class CreateCreditCardSchema(CreditCardSchema, CustomerTypeSchema):
billing = AddressSchema(missing=colander.drop)


class UpdateCreditCardSchema(CreateCreditCardSchema):
card_number = colander.SchemaNode(colander.String(),
validator=colander.Length(min=4),
required=True)


class ValidateCreditCardSchema(colander.MappingSchema):
address_id = colander.SchemaNode(colander.String(),
validator=colander.Length(max=60),
Expand Down
60 changes: 60 additions & 0 deletions tests/test_credit_card_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@
},
}

# Update the credit card information except the card number
UPDATE_CREDIT_CARD_INFO = {
'customer_type': 'individual',
'card_number': '1111',
'card_code': '456',
'expiration_month': '04',
'expiration_year': '2014',
'billing': {
'first_name': 'Rob',
'last_name': 'Oteron',
'company': 'Robotron Studios',
'address': '101 Computer Street',
'city': 'Tucson',
'state': 'AZ',
'zip': '85704',
'country': 'US',
'phone_number': '520-123-4567',
'fax_number': '520-456-7890',
},
}

VALIDATE_CREDIT_CARD = {
'address_id': '7982053235',
'card_code': '456',
Expand Down Expand Up @@ -108,6 +129,40 @@
</updateCustomerPaymentProfileRequest>
'''

UPDATE_CREDIT_CARD_INFO_REQUEST = '''
<?xml version="1.0" ?>
<updateCustomerPaymentProfileRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
<merchantAuthentication>
<name>8s8tVnG5t</name>
<transactionKey>5GK7mncw8mG2946z</transactionKey>
</merchantAuthentication>
<customerProfileId>1234567890</customerProfileId>
<paymentProfile>
<customerType>individual</customerType>
<billTo>
<firstName>Rob</firstName>
<lastName>Oteron</lastName>
<company>Robotron Studios</company>
<address>101 Computer Street</address>
<city>Tucson</city>
<state>AZ</state>
<zip>85704</zip>
<country>US</country>
<phoneNumber>520-123-4567</phoneNumber>
<faxNumber>520-456-7890</faxNumber>
</billTo>
<payment>
<creditCard>
<cardNumber>XXXX1111</cardNumber>
<expirationDate>2014-04</expirationDate>
<cardCode>456</cardCode>
</creditCard>
</payment>
<customerPaymentProfileId>0987654321</customerPaymentProfileId>
</paymentProfile>
</updateCustomerPaymentProfileRequest>
'''

DELETE_CREDIT_CARD_REQUEST = '''
<?xml version="1.0" ?>
<deleteCustomerPaymentProfileRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
Expand Down Expand Up @@ -155,6 +210,11 @@ def test_update_credit_card_request(self):
request_string = prettify(request_xml)
self.assertEqual(request_string, UPDATE_CREDIT_CARD_REQUEST.strip())

def test_update_credit_card_info_request(self):
request_xml = Configuration.api.credit_card._update_request('1234567890', '0987654321', UPDATE_CREDIT_CARD_INFO)
request_string = prettify(request_xml)
self.assertEqual(request_string, UPDATE_CREDIT_CARD_INFO_REQUEST.strip())

def test_delete_credit_card_request(self):
request_xml = Configuration.api.credit_card._delete_request('1234567890', '0987654321')
request_string = prettify(request_xml)
Expand Down
31 changes: 30 additions & 1 deletion tests/test_live_credit_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,30 @@
},
}

UPDATE_CREDIT_CARD = {
'card_number': '5555555555554444',
'expiration_date': '04/{0}'.format(date.today().year + 1),
'card_code': '567',
}

UPDATE_CREDIT_CARD_WITH_MASK = {
'card_number': 'XXXX4444',
'expiration_date': '04/{0}'.format(date.today().year + 1),
'card_code': '567',
}

UPDATE_CREDIT_CARD_WITHOUT_MASK = {
'card_number': '4444',
'expiration_date': '04/{0}'.format(date.today().year + 1),
'card_code': '567',
}

UPDATE_CREDIT_CARD_INVALID_MASK = {
'card_number': '1111',
'expiration_date': '04/{0}'.format(date.today().year + 1),
'card_code': '567',
}

PAYMENT_RESULT = {
'credit_card': {
'card_number': 'XXXX1111',
Expand Down Expand Up @@ -61,7 +85,12 @@ def test_live_basic_credit_card(self):
self.assertEquals(PAYMENT_RESULT, result.payment_profile.payment)

# Update credit card
CreditCard.update(customer_id, payment_id, CREDIT_CARD)
CreditCard.update(customer_id, payment_id, UPDATE_CREDIT_CARD)
CreditCard.update(customer_id, payment_id, UPDATE_CREDIT_CARD_WITH_MASK)
CreditCard.update(customer_id, payment_id, UPDATE_CREDIT_CARD_WITHOUT_MASK)

# Invalid masked number
self.assertRaises(AuthorizeResponseError, CreditCard.update, customer_id, payment_id, UPDATE_CREDIT_CARD_INVALID_MASK)

# Delete tests
CreditCard.delete(customer_id, payment_id)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
'expiration_date': '04/{0}'.format(date.today().year + 1),
}

UPDATE_CREDIT_CARD_EXP_MONTH_AND_YEAR = {
'card_number': '4111111111111111',
'card_code': '456',
'expiration_month': '04',
'expiration_year': str(date.today().year + 1)
}

INVALID_CREDIT_CARD_NUMBER = {
'card_number': 'Bad card number',
'card_code': '456',
Expand Down

0 comments on commit 5107dd8

Please sign in to comment.