def test_purchase_should_return_invalid_sandbox_card_error(self): data = { 'custom': '', 'first_name': '', 'last_name': '', 'address_1': '', 'address_2': '', 'city': '', 'state': '', 'zip': '10101', 'card_number': '5256486068715680', 'cvv': '111', 'expiry_month': '05', 'expiry_year': '2014', } token = test_helper.default_payment_method(data).payment_method_token purchase = Processor.purchase(token, 1.00) self.assertIn( { 'context': 'system.general', 'key': 'default', 'subclass': 'error', 'text': 'Invalid Sandbox Card Number. For more information, see: https://samurai.feefighters.com/developers/sandbox' }, purchase.error_messages)
def test_cvv_should_return_processor_cvv_result_code_N(self): token = test_helper.default_payment_method({'cvv':'222'}).payment_method_token purchase = Processor.purchase(token, 1.00, billing_reference=self.rand) self.assertTrue(purchase.success) self.assertEqual(purchase.processor_response['cvv_result_code'], 'N')
def test_cvv_should_return_processor_cvv_result_code_N(self): token = test_helper.default_payment_method({ 'cvv': '222' }).payment_method_token purchase = Processor.purchase(token, 1.00, billing_reference=self.rand) self.assertTrue(purchase.success) self.assertEqual(purchase.processor_response['cvv_result_code'], 'N')
def test_should_return_processor_avs_result_code_N(self): token = test_helper.default_payment_method({'address_1':'123 Main St', 'address_2':'', 'zip':'60610'}).payment_method_token purchase = Processor.purchase(token, 1.00, billing_reference=self.rand) self.assertTrue(purchase.success) self.assertEqual(purchase.processor_response['avs_result_code'], 'N')
def test_should_return_processor_avs_result_code_N(self): token = test_helper.default_payment_method({ 'address_1': '123 Main St', 'address_2': '', 'zip': '60610' }).payment_method_token purchase = Processor.purchase(token, 1.00, billing_reference=self.rand) self.assertTrue(purchase.success) self.assertEqual(purchase.processor_response['avs_result_code'], 'N')
def test_purchase_should_return_input_amount_invalid(self): token = self.pm.payment_method_token purchase = Processor.purchase(token, 1.10, billing_reference=self.rand) self.assertFalse(purchase.success) err = {'context': 'input.amount', 'key': 'invalid', 'subclass': 'error'} self.assertIn(err, purchase.error_messages) self.assertIn('The transaction amount was invalid.', purchase.errors['input.amount'])
def test_purchase_should_return_processor_transaction_declined(self): token = self.pm.payment_method_token purchase = Processor.purchase(token, 1.02, billing_reference=self.rand) self.assertFalse(purchase.success) err = {'context': 'processor.transaction', 'key': 'declined', 'subclass': 'error'} self.assertIn(err, purchase.error_messages) self.assertIn('The card was declined.' , purchase.errors['processor.transaction'])
def purchase(request): token = request.GET.get('payment_method_token', None) trans = Processor.purchase(token, 10) if trans.errors: errors = parse_error(trans.errors) for err in errors: messages.error(request, err, fail_silently=True) return redirect('/transparent_redirect/payment_form') else: messages.success(request, 'Purchase Successful.', fail_silently=True) return render_to_response('/transparent_redirect/receipt.html')
def purchase(request): if request.method == 'POST': token = request.POST.get('payment_method_token', None) trans = Processor.purchase(token, 10) if trans.errors: success=False else: success=True return_data = simplejson.dumps({'transaction':{'success':success}}) return HttpResponse(return_data) else: return redirect('/samurai_js/payment_form')
def capture(self, amount, credit_card=None, billing_info=None, shipping_info=None): # set up the card for charging, obviously card_token = self.charge_setup(credit_card, billing_info) # start the timer start = time.time() # send it over for processing response = Processor.purchase(card_token, amount) # measure time end = time.time() # done timing it response_time = '%0.2f' % (end-start) # return parsed response return self.parse(response, response_time)
def purchase(self, money, credit_card): if not self.validate_card(credit_card): raise InvalidCard("Invalid Card") try: from samurai.payment_method import PaymentMethod from samurai.processor import Processor pm = PaymentMethod.create(credit_card.number, credit_card.verification_value, credit_card.month, credit_card.year) payment_method_token = pm.payment_method_token response = Processor.purchase(payment_method_token, money) except Exception, error: return {'status': 'FAILURE', 'response': error}
def capture(self, amount, credit_card=None, billing_info=None, shipping_info=None): # set up the card for charging, obviously card_token = self.charge_setup(credit_card, billing_info) # start the timer start = time.time() # send it over for processing response = Processor.purchase(card_token, amount) # measure time end = time.time() # done timing it response_time = '%0.2f' % (end-start) # return parsed response return self.parse(response, response_time)
def test_purchase_should_return_processor_transaction_declined(self): token = self.pm.payment_method_token purchase = Processor.purchase(token, 1.02, billing_reference=self.rand) self.assertFalse(purchase.success) err = { 'context': 'processor.transaction', 'key': 'declined', 'subclass': 'error' } self.assertIn(err, purchase.error_messages) self.assertIn('The card was declined.', purchase.errors['processor.transaction'])
def test_purchase_should_return_input_amount_invalid(self): token = self.pm.payment_method_token purchase = Processor.purchase(token, 1.10, billing_reference=self.rand) self.assertFalse(purchase.success) err = { 'context': 'input.amount', 'key': 'invalid', 'subclass': 'error' } self.assertIn(err, purchase.error_messages) self.assertIn('The transaction amount was invalid.', purchase.errors['input.amount'])
def purchase(self, money, credit_card): if not self.validate_card(credit_card): raise InvalidCard("Invalid Card") try: from samurai.payment_method import PaymentMethod from samurai.processor import Processor pm = PaymentMethod.create(credit_card.number, credit_card.verification_value, credit_card.month, credit_card.year) payment_method_token = pm.payment_method_token response = Processor.purchase(payment_method_token, money) except Exception, error: return {'status': 'FAILURE', 'response': error}
def purchase(self, money, credit_card): # Cases where token is directly sent for e.g. from Samurai.js payment_method_token = credit_card if isinstance(credit_card, CreditCard): if not self.validate_card(credit_card): raise InvalidCard("Invalid Card") pm = PaymentMethod.create(credit_card.number, credit_card.verification_value, credit_card.month, credit_card.year) payment_method_token = pm.payment_method_token response = Processor.purchase(payment_method_token, money) if response.errors: return {'status': 'FAILURE', 'response': response} return {'status': 'SUCCESS', 'response': response}
def purchase(request): if request.method == "POST": data = request.POST token = PaymentMethod.create(data.get('card_number'), data.get('cvv'), data.get('expiry_month'), data.get('expiry_year'), first_name=data.get('first_name'), last_name=data.get('last_name')) trans = Processor.purchase(token.payment_method_token, 10) if trans.errors: errors = parse_error(trans.errors) for err in errors: messages.error(request, err, fail_silently=True) return redirect('/server_to_server/payment_form') else: messages.success(request, 'Purchase Successful.', fail_silently=True) return render_to_response('/server_to_server/receipt.html') else: return redirect('/server_to_server/payment_form')
def test_purchase_should_be_successful(self): options = {'description':'description', 'descriptor_name':'descriptor_name', 'descriptor_phone':'descriptor_phone', 'custom':'custom', 'billing_reference':'ABC123%s' % self.rand, 'customer_reference':'Customer (123)'} token = self.pm.payment_method_token purchase = Processor.purchase(token, 10.0, None, **options) self.assertTrue(purchase.success) self.assertEquals(purchase.error_messages, []) self.assertEqual(purchase.description, 'description') self.assertEqual(purchase.descriptor_name, 'descriptor_name') self.assertEqual(purchase.descriptor_phone, 'descriptor_phone') self.assertEqual(purchase.custom, 'custom') self.assertEqual(purchase.billing_reference, 'ABC123%s' % self.rand) self.assertEqual(purchase.customer_reference, 'Customer (123)')
def test_purchase_should_return_invalid_sandbox_card_error(self): data = { 'custom' : '', 'first_name' : '', 'last_name' : '', 'address_1' : '', 'address_2' : '', 'city' : '', 'state' : '', 'zip' : '10101', 'card_number' : '5256486068715680', 'cvv' : '111', 'expiry_month' : '05', 'expiry_year' : '2014', } token = test_helper.default_payment_method(data).payment_method_token purchase = Processor.purchase(token, 1.00) self.assertIn({'context': 'system.general', 'key': 'default', 'subclass': 'error', 'text': 'Invalid Sandbox Card Number. For more information, see: https://samurai.feefighters.com/developers/sandbox'}, purchase.error_messages)
def test_purchase_should_be_successful(self): options = { 'description': 'description', 'descriptor_name': 'descriptor_name', 'descriptor_phone': 'descriptor_phone', 'custom': 'custom', 'billing_reference': 'ABC123%s' % self.rand, 'customer_reference': 'Customer (123)' } token = self.pm.payment_method_token purchase = Processor.purchase(token, 10.0, None, **options) self.assertTrue(purchase.success) self.assertEquals(purchase.error_messages, []) self.assertEqual(purchase.description, 'description') self.assertEqual(purchase.descriptor_name, 'descriptor_name') self.assertEqual(purchase.descriptor_phone, 'descriptor_phone') self.assertEqual(purchase.custom, 'custom') self.assertEqual(purchase.billing_reference, 'ABC123%s' % self.rand) self.assertEqual(purchase.customer_reference, 'Customer (123)')
def test_purchase_should_return_payment_method_errors_on_blank_pm(self): data = { 'custom' : '', 'first_name' : '', 'last_name' : '', 'address_1' : '', 'address_2' : '', 'city' : '', 'state' : '', 'zip' : '', 'card_number' : '', 'cvv' : '', 'expiry_month' : '05', 'expiry_year' : '2014', } token = test_helper.default_payment_method(data).payment_method_token purchase = Processor.purchase(token, 1.00) self.assertFalse(purchase.success) self.assertIn({'context': 'input.card_number', 'key': 'not_numeric', 'subclass': 'error'}, purchase.error_messages) self.assertIn({'context': 'input.card_number', 'key': 'too_short', 'subclass': 'error'}, purchase.error_messages) self.assertIn({'context': 'input.card_number', 'key': 'is_blank', 'subclass': 'error'}, purchase.error_messages)
def test_purchase_should_return_payment_method_errors_on_blank_pm(self): data = { 'custom': '', 'first_name': '', 'last_name': '', 'address_1': '', 'address_2': '', 'city': '', 'state': '', 'zip': '', 'card_number': '', 'cvv': '', 'expiry_month': '05', 'expiry_year': '2014', } token = test_helper.default_payment_method(data).payment_method_token purchase = Processor.purchase(token, 1.00) self.assertFalse(purchase.success) self.assertIn( { 'context': 'input.card_number', 'key': 'not_numeric', 'subclass': 'error' }, purchase.error_messages) self.assertIn( { 'context': 'input.card_number', 'key': 'too_short', 'subclass': 'error' }, purchase.error_messages) self.assertIn( { 'context': 'input.card_number', 'key': 'is_blank', 'subclass': 'error' }, purchase.error_messages)
def test_purchase_failure(self): token = self.pm.payment_method_token trans = Processor.purchase(token, 10.02) errors = [{'context': 'processor.transaction', 'key': 'declined', 'subclass': 'error'}] self.assertEquals(trans.errors, errors)
def charge(participant_id, pmt, amount): """Given two unicodes and a Decimal, return a boolean indicating success. This is the only place where we actually charge credit cards. Amount should be the nominal amount. We compute Gittip's fee in this function and add it to amount. """ typecheck( pmt, (unicode, None) , participant_id, unicode , amount, decimal.Decimal ) if pmt is None: STATS = """\ UPDATE paydays SET npmt_missing = npmt_missing + 1 WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id """ assert_one_payday(db.fetchone(STATS)) return False # We have a purported payment method token. Try to use it. # ======================================================== charge_amount = (amount + FEE[0]) * FEE[1] charge_amount = charge_amount.quantize(FEE[0], rounding=decimal.ROUND_UP) fee = charge_amount - amount log("Charging %s $%s + $%s fee = $%s." % (participant_id, amount, fee, charge_amount)) transaction = Processor.purchase(pmt, charge_amount, custom=participant_id) # XXX If the power goes out at this point then Postgres will be out of sync # with Samurai. We'll have to resolve that manually be reviewing the # Samurai transaction log and modifying Postgres accordingly. with db.get_connection() as conn: cur = conn.cursor() if transaction.errors: last_bill_result = json.dumps(transaction.errors) amount = decimal.Decimal('0.00') STATS = """\ UPDATE paydays SET npmt_failing = npmt_failing + 1 WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id """ cur.execute(STATS) assert_one_payday(cur.fetchone()) else: last_bill_result = '' EXCHANGE = """\ INSERT INTO exchanges (amount, fee, participant_id) VALUES (%s, %s, %s) """ cur.execute(EXCHANGE, (amount, fee, participant_id)) STATS = """\ UPDATE paydays SET nexchanges = nexchanges + 1 , exchange_volume = exchange_volume + %s , exchange_fees_volume = exchange_fees_volume + %s WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id """ cur.execute(STATS, (charge_amount, fee)) assert_one_payday(cur.fetchone()) # Update the participant's balance. # ================================= # Credit card charges go immediately to balance, not to pending. RESULT = """\ UPDATE participants SET last_bill_result=%s , balance=(balance + %s) WHERE id=%s """ cur.execute(RESULT, (last_bill_result, amount, participant_id)) conn.commit() return not bool(last_bill_result) # True indicates success
def setUp(self): self.pm = test_helper.default_payment_method() self.rand = randint(100, 999) self.auth = Processor.authorize(self.pm.payment_method_token, 100.0) self.purchase = Processor.purchase(self.pm.payment_method_token, 100.0)
def test_purchase(self): token = self.pm.payment_method_token trans = Processor.purchase(token, 10.0) self.assertTrue(trans.success) self.assertEquals(trans.errors, [])
def test_purchase_partial_reverse(self): purchase = Processor.purchase(self.pm.payment_method_token, 10.0) trans = purchase.reverse(5.0) self.assertTrue(trans.success) self.assertEquals(trans.errors, []) self.assertEquals(trans.amount, '5.0')
def setUp(self): self.pm = test_helper.default_payment_method() self.rand = randint(100, 999) self.auth = Processor.authorize(self.pm.payment_method_token, 100.0) self.purchase = Processor.purchase(self.pm.payment_method_token, 100.0)
def charge(participant_id, pmt, amount): """Given two unicodes and a Decimal, return a boolean indicating success. This is the only place where we actually charge credit cards. Amount should be the nominal amount. We compute Gittip's fee in this function and add it to amount. """ typecheck(pmt, (unicode, None), participant_id, unicode, amount, decimal.Decimal) if pmt is None: STATS = """\ UPDATE paydays SET npmt_missing = npmt_missing + 1 WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id """ assert_one_payday(db.fetchone(STATS)) return False # We have a purported payment method token. Try to use it. # ======================================================== charge_amount = (amount + FEE[0]) * FEE[1] charge_amount = charge_amount.quantize(FEE[0], rounding=decimal.ROUND_UP) fee = charge_amount - amount log("Charging %s $%s + $%s fee = $%s." % (participant_id, amount, fee, charge_amount)) transaction = Processor.purchase(pmt, charge_amount, custom=participant_id) # XXX If the power goes out at this point then Postgres will be out of sync # with Samurai. We'll have to resolve that manually be reviewing the # Samurai transaction log and modifying Postgres accordingly. with db.get_connection() as conn: cur = conn.cursor() if transaction.errors: last_bill_result = json.dumps(transaction.errors) amount = decimal.Decimal('0.00') STATS = """\ UPDATE paydays SET npmt_failing = npmt_failing + 1 WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id """ cur.execute(STATS) assert_one_payday(cur.fetchone()) else: last_bill_result = '' EXCHANGE = """\ INSERT INTO exchanges (amount, fee, participant_id) VALUES (%s, %s, %s) """ cur.execute(EXCHANGE, (amount, fee, participant_id)) STATS = """\ UPDATE paydays SET nexchanges = nexchanges + 1 , exchange_volume = exchange_volume + %s , exchange_fees_volume = exchange_fees_volume + %s WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id """ cur.execute(STATS, (charge_amount, fee)) assert_one_payday(cur.fetchone()) # Update the participant's balance. # ================================= # Credit card charges go immediately to balance, not to pending. RESULT = """\ UPDATE participants SET last_bill_result=%s , balance=(balance + %s) WHERE id=%s """ cur.execute(RESULT, (last_bill_result, amount, participant_id)) conn.commit() return not bool(last_bill_result) # True indicates success
def test_purchase_partial_reverse(self): purchase = Processor.purchase(self.pm.payment_method_token, 10.0) trans = purchase.reverse(5.0) self.assertTrue(trans.success) self.assertEquals(trans.errors, []) self.assertEquals(trans.amount, '5.0')