diff --git a/README.markdown b/README.markdown index b1fcf92..d08551c 100644 --- a/README.markdown +++ b/README.markdown @@ -70,6 +70,7 @@ credit cards given for sandbox use. Example config file: # these should match your beanstream account settings + [config] hash_validation: true require_billing_address: true require_cvd: true diff --git a/beanstream/gateway.py b/beanstream/gateway.py index 7e16e7f..cce7396 100644 --- a/beanstream/gateway.py +++ b/beanstream/gateway.py @@ -75,10 +75,10 @@ def configure(self, merchant_id, login_company, login_user, login_password, **pa if self.HASH_VALIDATION and self.hash_algorithm not in ('MD5', 'SHA1'): raise errors.ConfigurationException('hash algorithm must be one of MD5 or SHA1') - def purchase(self, amount, card, billing_address=None): + def purchase(self, amount, card, billing_address=None, order_number=None): """ Returns a Purchase object with the specified options. """ - txn = process_transaction.Purchase(self, amount) + txn = process_transaction.Purchase(self, amount, order_number) txn.set_card(card) if billing_address: txn.set_billing_address(billing_address) @@ -89,14 +89,14 @@ def void_purchase(self, transaction_id, amount): """ Returns an Adjustment object configured for voiding the specified transaction for the specified amount. """ - txn = process_transaction.Adjustment(self, process_transaction.Adjustment.VOID, transaction_id, amount) + txn = process_transaction.Adjustment(self, process_transaction.Adjustment.VOID_PURCHASE, transaction_id, amount) return txn - def return_purchase(self, transaction_id, amount): + def return_purchase(self, transaction_id, amount, order_number=None): """ Returns an Adjustment object configured for returning the specified transaction for the specified amount. """ - txn = process_transaction.Adjustment(self, process_transaction.Adjustment.RETURN, transaction_id, amount) + txn = process_transaction.Adjustment(self, process_transaction.Adjustment.RETURN, transaction_id, amount, order_number) return txn def void_return(self, transaction_id, amount): @@ -106,21 +106,21 @@ def void_return(self, transaction_id, amount): txn = process_transaction.Adjustment(self, process_transaction.Adjustment.VOID_RETURN, transaction_id, amount) return txn - def preauth(self, amount, card, billing_address=None): + def preauth(self, amount, card, billing_address=None, order_number=None): """ Returns a PreAuthorization object with the specified options. """ - txn = process_transaction.PreAuthorization(self, amount) + txn = process_transaction.PreAuthorization(self, amount, order_number) txn.set_card(card) if billing_address: txn.set_billing_address(billing_address) return txn - def preauth_completion(self, transaction_id, amount): + def preauth_completion(self, transaction_id, amount, order_number=None): """ Returns an Adjustment object configured for completing the preauthorized transaction for the specified amount. """ - txn = process_transaction.Adjustment(self, process_transaction.Adjustment.PREAUTH_COMPLETION, transaction_id, amount) + txn = process_transaction.Adjustment(self, process_transaction.Adjustment.PREAUTH_COMPLETION, transaction_id, amount, order_number) return txn def cancel_preauth(self, transaction_id): @@ -151,10 +151,10 @@ def get_payment_profile(self, customer_code): txn = payment_profiles.GetPaymentProfile(self, customer_code) return txn - def purchase_with_payment_profile(self, amount, customer_code): + def purchase_with_payment_profile(self, amount, customer_code, order_number=None): """ Returns a Purchase object with the specified options. """ - txn = process_transaction.Purchase(self, amount) + txn = process_transaction.Purchase(self, amount, order_number) txn.set_customer_code(customer_code) return txn diff --git a/beanstream/process_transaction.py b/beanstream/process_transaction.py index dd744ed..665e07a 100644 --- a/beanstream/process_transaction.py +++ b/beanstream/process_transaction.py @@ -24,8 +24,8 @@ class Purchase(transaction.Transaction): - def __init__(self, beanstream_gateway, amount): - super(Purchase, self).__init__(beanstream_gateway) + def __init__(self, beanstream_gateway, amount, order_number=None): + super(Purchase, self).__init__(beanstream_gateway, order_number) self.url = self.URLS['process_transaction'] self.response_class = PurchaseResponse @@ -125,13 +125,13 @@ def auth_code(self): class PreAuthorization(Purchase): - def __init__(self, beanstream_gateway, amount): - super(PreAuthorization, self).__init__(beanstream_gateway, amount) + def __init__(self, beanstream_gateway, amount, order_number=None): + super(PreAuthorization, self).__init__(beanstream_gateway, amount, order_number) self.params['trnType'] = self.TRN_TYPES['preauth'] -class Adjustment(transaction.Transaction): +class Adjustment(Purchase): RETURN = 'R' VOID = 'V' @@ -139,8 +139,8 @@ class Adjustment(transaction.Transaction): VOID_RETURN = 'VR' VOID_PURCHASE = 'VP' - def __init__(self, beanstream_gateway, adjustment_type, transaction_id, amount): - super(PreAuthorizationCompletion, self).__init__(beanstream_gateway) + def __init__(self, beanstream_gateway, adjustment_type, transaction_id, amount, order_number=None): + super(Adjustment, self).__init__(beanstream_gateway, amount, order_number) if not beanstream_gateway.HASH_VALIDATION and not beanstream_gateway.USERNAME_VALIDATION: raise errors.ConfigurationException('adjustments must be performed with either hash or username/password validation') diff --git a/beanstream/response_codes.py b/beanstream/response_codes.py index 494cfae..e4cf173 100644 --- a/beanstream/response_codes.py +++ b/beanstream/response_codes.py @@ -782,6 +782,7 @@ '787': {'type': 'TD Bank', 'approved': False, 'cardholder_message': 'Declined', 'merchant_message': 'Declined'}, '788': {'type': 'Beanstream', 'approved': False, 'cardholder_message': 'Duplicate Order Number - This order number has already been processed', 'merchant_message': 'Duplicate Order Number - This order number has already been processed'}, '789': {'type': 'Beanstream', 'approved': False, 'cardholder_message': 'Card Number Mismatch', 'merchant_message': 'Card Number Mismatch'}, + '804': {'type': 'Beanstream', 'approved': False, 'cardholder_message': 'Entered information cannot be authenticated', 'merchant_message': 'Entered information cannot be authenticated'}, } diff --git a/beanstream/transaction.py b/beanstream/transaction.py index 5ec35b9..8d511a9 100644 --- a/beanstream/transaction.py +++ b/beanstream/transaction.py @@ -50,7 +50,7 @@ class Transaction(object): } - def __init__(self, beanstream): + def __init__(self, beanstream, order_number=None): self.beanstream = beanstream self.response_class = Response @@ -60,7 +60,10 @@ def __init__(self, beanstream): self.params['username'] = self.beanstream.username self.params['password'] = self.beanstream.password - self._generate_order_number() + if order_number: + self.order_number = order_number + else: + self._generate_order_number() self.params['trnOrderNumber'] = self.order_number self.response_params = [] @@ -90,7 +93,7 @@ def commit(self): log.debug('Sending to %s: %s', self.url, data) - res = urllib2.urlopen(self.url, data) + res = urllib2.urlopen(self.url, data, 10) if res.code != 200: log.error('response code not OK: %s', res.code) diff --git a/tests/simple_t.py b/tests/simple_t.py index 09eab68..2feab4d 100644 --- a/tests/simple_t.py +++ b/tests/simple_t.py @@ -108,8 +108,7 @@ def test_failed_cvd(self): txn.set_comments('%s:test_failed_cvd' % __name__) resp = txn.commit() assert not resp.approved() - assert resp.cvd_status() == 'CVD Mismatch' - + assert resp.cvd_status() == 'CVD Not Verified' def test_over_limit_cc_purchase(self): today = date.today()