def ipn(): """Endpoint that receives Instant Payment Notifications (IPNs) from WePay. More info is available at https://www.wepay.com/developer/reference/ipn. """ # Checking the type of object (should be `checkout`) if 'checkout_id' in request.form: checkout_id = request.form['checkout_id'] else: # No need to return any errors in this case # TODO: Add logging there. return 'Invalid object_id.', 200 # Getting additional info that was passed with callback_uri during payment creation if 'is_donation' in request.args and request.args['is_donation'] != 'True': result = Payment.verify_and_log_wepay_checkout( checkout_id=checkout_id, is_donation=False, invoice_number=int(request.args['invoice_number']), ) else: result = Payment.verify_and_log_wepay_checkout( checkout_id=checkout_id, is_donation=True, editor=request.args['editor'], anonymous=request.args['anonymous'], can_contact=request.args['can_contact'], ) if result is True: return "Recorded." return '', 200
def test_currency_case(self): """Try with different letter cases in the currency value.""" form = copy.deepcopy(self.base_form) form["mc_currency"] = "UsD" Payment.process_paypal_ipn(form) payments = Payment.query.all() self.assertEqual(payments[0].currency, Currency.US_Dollar.value)
def ipn(): """Endpoint that receives Instant Payment Notifications (IPNs) from PayPal. Options that we use are: 0 - can contact flag 1 - anonymous flag 2 - is donation flag 4 - invoice number Specifications are available at https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/. """ request.parameter_storage_class = ImmutableOrderedMultiDict # Checking if data is legit paypal_url = PAYPAL_URL_PRIMARY if current_app.config[ 'PAYMENT_PRODUCTION'] else PAYPAL_URL_SANDBOX verify_args = chain(IPN_VERIFY_EXTRA_PARAMS, request.form.items()) verify_string = u'&'.join( (u'%s=%s' % (param, value) for param, value in verify_args)) verification_response = requests.post(paypal_url, data=verify_string.encode('utf-8')) # Some payment options don't return payment_status value. if 'payment_status' not in request.form: logging.warn('PayPal IPN: payment_status is missing.') return '', 200 if verification_response.text == 'VERIFIED': Payment.process_paypal_ipn(request.form) return '', 200
def ipn(): """Endpoint that receives Instant Payment Notifications (IPNs) from PayPal. Options that we use are: 0 - can contact flag 1 - anonymous flag 2 - is donation flag 4 - invoice number Specifications are available at https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/. """ request.parameter_storage_class = ImmutableOrderedMultiDict # Checking if data is legit paypal_url = PAYPAL_URL_PRIMARY if current_app.config['PAYMENT_PRODUCTION'] else PAYPAL_URL_SANDBOX verify_args = chain(IPN_VERIFY_EXTRA_PARAMS, request.form.iteritems()) verify_string = u'&'.join((u'%s=%s' % (param, value) for param, value in verify_args)) verification_response = requests.post(paypal_url, data=verify_string.encode('utf-8')) # Some payment options don't return payment_status value. if 'payment_status' not in request.form: logging.warn('PayPal IPN: payment_status is missing.') return '', 200 if verification_response.text == 'VERIFIED': Payment.process_paypal_ipn(request.form) return '', 200
def donors(): page = int(request.args.get('page', default=1)) if page < 1: return redirect(url_for('.donors')) limit = 30 offset = (page - 1) * limit order = request.args.get('order', default='date') if order == 'date': count, donations = Payment.get_recent_donations(limit=limit, offset=offset) elif order == 'amount': count, donations = Payment.get_biggest_donations(limit=limit, offset=offset) else: return redirect(url_for('.donors')) last_page = int(ceil(count / limit)) if last_page != 0 and page > last_page: return redirect(url_for('.donors', page=last_page)) return render_template('payments/donors.html', donations=donations, page=page, last_page=last_page, order=order)
def test_unknown_currency(self): form = copy.deepcopy(self.base_form) form["mc_currency"] = "rub" Payment.process_paypal_ipn(form) payments = Payment.query.all() # Should ignore this payment notification self.assertEqual(len(payments), 0)
def ipn(): """Endpoint that receives Instant Payment Notifications (IPNs) from WePay. More info is available at https://www.wepay.com/developer/reference/ipn. """ # Checking the type of object (should be `checkout`) if "checkout_id" in request.form: checkout_id = request.form["checkout_id"] else: # No need to return any errors in this case # TODO: Add logging there. return "Invalid object_id.", 200 # Getting additional info that was passed with callback_uri during payment creation if "is_donation" in request.args and request.args["is_donation"] != "True": result = Payment.verify_and_log_wepay_checkout( checkout_id=checkout_id, is_donation=False, invoice_number=int(request.args["invoice_number"]) ) else: result = Payment.verify_and_log_wepay_checkout( checkout_id=checkout_id, is_donation=True, editor=request.args["editor"], anonymous=request.args["anonymous"], can_contact=request.args["can_contact"], ) if result is True: return "Recorded." return "", 200
def pay(): """Payment page for Stripe. Stripe API reference for Python can be found at https://stripe.com/docs/api/python. """ is_donation = request.args.get("donation") == "True" if is_donation: form = DonationForm() else: form = PaymentForm() if not form.validate(): return redirect(url_for('payments.error', is_donation=is_donation)) if current_app.config['PAYMENT_PRODUCTION']: stripe.api_key = current_app.config['STRIPE_KEYS']['SECRET'] else: stripe.api_key = current_app.config['STRIPE_TEST_KEYS']['SECRET'] # Get the credit card details submitted by the form token = request.form['stripeToken'] charge_metadata = { 'email': request.form['stripeEmail'], 'is_donation': is_donation, } if is_donation: charge_description = "Donation to the MetaBrainz Foundation" # Using DonationForm charge_metadata['editor'] = form.editor.data charge_metadata['anonymous'] = form.anonymous.data charge_metadata['can_contact'] = form.can_contact.data else: charge_description = "Payment to the MetaBrainz Foundation" # Using PaymentForm charge_metadata['invoice_number'] = form.invoice_number.data # Create the charge on Stripe's servers - this will charge the donor's card try: charge = stripe.Charge.create( amount=int(form.amount.data * 100), # amount in cents currency='USD', card=token, description=charge_description, metadata=charge_metadata, ) except stripe.CardError as e: # The card has been declined logging.info(e) return redirect(url_for('payments.error', is_donation=is_donation)) Payment.log_stripe_charge(charge) return redirect(url_for('payments.complete', is_donation=is_donation))
def test_process_paypal_ipn(self): Payment.process_paypal_ipn(self.base_form) # Donation should be in the DB now payments = Payment.query.all() self.assertEqual(len(payments), 1) self.assertEqual(payments[0].transaction_id, "TEST1") relatively_bad_form = copy.deepcopy(self.base_form) relatively_bad_form["txn_id"] = "TEST2" relatively_bad_form["mc_gross"] = "0.49" Payment.process_paypal_ipn(relatively_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) bad_form = copy.deepcopy(self.base_form) relatively_bad_form["txn_id"] = "TEST3" bad_form["business"] = current_app.config["PAYPAL_BUSINESS"] Payment.process_paypal_ipn(bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) super_bad_form = copy.deepcopy(self.base_form) relatively_bad_form["txn_id"] = "TEST4" super_bad_form["payment_status"] = "Refunded" # What kind of monster would do that?! Payment.process_paypal_ipn(super_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1)
def test_process_paypal_ipn(self): Payment.process_paypal_ipn(self.base_form) # Donation should be in the DB now payments = Payment.query.all() self.assertEqual(len(payments), 1) self.assertEqual(payments[0].transaction_id, "TEST1") relatively_bad_form = copy.deepcopy(self.base_form) relatively_bad_form["txn_id"] = "TEST2" relatively_bad_form["mc_gross"] = "0.49" Payment.process_paypal_ipn(relatively_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) bad_form = copy.deepcopy(self.base_form) relatively_bad_form["txn_id"] = "TEST3" bad_form["business"] = current_app.config["PAYPAL_BUSINESS"] Payment.process_paypal_ipn(bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) super_bad_form = copy.deepcopy(self.base_form) relatively_bad_form["txn_id"] = "TEST4" super_bad_form[ "payment_status"] = "Refunded" # What kind of monster would do that?! Payment.process_paypal_ipn(super_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1)
def test_get_by_transaction_id(self): new = Payment() new.is_donation = True new.first_name = u'Tester' new.last_name = u'Testing' new.email = u'*****@*****.**' new.transaction_id = u'TEST' new.amount = 42.50 db.session.add(new) db.session.commit() result = Payment.get_by_transaction_id(u'TEST') self.assertIsNotNone(result) bad_result = Payment.get_by_transaction_id(u'MISSING') self.assertIsNone(bad_result)
def test_get_by_transaction_id(self): new = Payment() new.is_donation = True new.first_name = "Tester" new.last_name = "Testing" new.email = "*****@*****.**" new.transaction_id = "TEST" new.amount = 42.50 new.currency = "eur" db.session.add(new) db.session.commit() result = Payment.get_by_transaction_id("TEST") self.assertIsNotNone(result) bad_result = Payment.get_by_transaction_id("MISSING") self.assertIsNone(bad_result)
def nag_check_with_param(): editor = request.args.get('editor') if not editor: raise BadRequest a, b = Payment.get_nag_days(editor) return '%s,%s\n' % (a, b)
def test_extract_paypal_ipn_options(self): form = copy.deepcopy(self.base_form) self.assertDictEqual(Payment._extract_paypal_ipn_options(form), { "anonymous": "yes", "contact": "yes", }) self.assertDictEqual(Payment._extract_paypal_ipn_options({ "option_name1": "contact", "option_name2": "anonymous", "option_name3": "is_donation", "option_selection1": "N/A", "option_selection2": "yes", "option_selection3": "yes", }), { "contact": "N/A", "anonymous": "yes", "is_donation": "yes", })
def test_verify_and_log_wepay_checkout_payment(self): checkout_id = 12345 self.assertTrue(Payment.verify_and_log_wepay_checkout( checkout_id=12345, is_donation=False, invoice_number=42, )) # Donation should be in the DB now self.assertEqual(Payment.query.all()[0].transaction_id, str(checkout_id))
def test_extract_paypal_ipn_options(self): form = copy.deepcopy(self.base_form) self.assertDictEqual(Payment._extract_paypal_ipn_options(form), { "anonymous": "yes", "contact": "yes", }) self.assertDictEqual( Payment._extract_paypal_ipn_options({ "option_name1": "contact", "option_name2": "anonymous", "option_name3": "is_donation", "option_selection1": "N/A", "option_selection2": "yes", "option_selection3": "yes", }), { "contact": "N/A", "anonymous": "yes", "is_donation": "yes", })
def test_process_paypal_ipn(self): # This is not a complete list: good_form = { 'first_name': u'Tester', 'last_name': u'Testing', 'custom': u'tester', # MusicBrainz username 'payer_email': u'*****@*****.**', 'receiver_email': current_app.config['PAYPAL_PRIMARY_EMAIL'], 'business': u'*****@*****.**', 'address_street': u'1 Main St', 'address_city': u'San Jose', 'address_state': u'CA', 'address_country': u'United States', 'address_zip': u'95131', 'mc_gross': u'42.50', 'mc_fee': u'1', 'txn_id': u'TEST1', 'payment_status': u'Completed', # Additional variables: 'option_name1': u'anonymous', 'option_selection1': u'yes', 'option_name2': u'contact', 'option_selection2': u'yes', } Payment.process_paypal_ipn(good_form) # Donation should be in the DB now self.assertEqual(len(Payment.query.all()), 1) self.assertEqual(Payment.query.all()[0].transaction_id, 'TEST1') relatively_bad_form = good_form relatively_bad_form['txn_id'] = 'TEST2' relatively_bad_form['mc_gross'] = '0.49' Payment.process_paypal_ipn(relatively_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) bad_form = good_form relatively_bad_form['txn_id'] = 'TEST3' bad_form['business'] = current_app.config['PAYPAL_BUSINESS'] Payment.process_paypal_ipn(bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) super_bad_form = good_form relatively_bad_form['txn_id'] = 'TEST4' super_bad_form[ 'payment_status'] = 'Refunded' # What kind of monster would do that?! Payment.process_paypal_ipn(super_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1)
def test_verify_and_log_wepay_checkout_donation(self): checkout_id = 12345 self.assertTrue(Payment.verify_and_log_wepay_checkout( checkout_id=12345, is_donation=True, editor='Tester', anonymous=False, can_contact=True, )) # Donation should be in the DB now self.assertEqual(Payment.query.all()[0].transaction_id, str(checkout_id))
def test_verify_and_log_wepay_checkout_payment(self): checkout_id = 12345 self.assertTrue( Payment.verify_and_log_wepay_checkout( checkout_id=12345, is_donation=False, invoice_number=42, )) # Donation should be in the DB now self.assertEqual(Payment.query.all()[0].transaction_id, str(checkout_id))
def test_process_paypal_ipn(self): # This is not a complete list: good_form = { 'first_name': u'Tester', 'last_name': u'Testing', 'custom': u'tester', # MusicBrainz username 'payer_email': u'*****@*****.**', 'receiver_email': current_app.config['PAYPAL_PRIMARY_EMAIL'], 'business': u'*****@*****.**', 'address_street': u'1 Main St', 'address_city': u'San Jose', 'address_state': u'CA', 'address_country': u'United States', 'address_zip': u'95131', 'mc_gross': u'42.50', 'mc_fee': u'1', 'txn_id': u'TEST1', 'payment_status': u'Completed', # Additional variables: 'option_name1': u'anonymous', 'option_selection1': u'yes', 'option_name2': u'contact', 'option_selection2': u'yes', } Payment.process_paypal_ipn(good_form) # Donation should be in the DB now self.assertEqual(len(Payment.query.all()), 1) self.assertEqual(Payment.query.all()[0].transaction_id, 'TEST1') relatively_bad_form = good_form relatively_bad_form['txn_id'] = 'TEST2' relatively_bad_form['mc_gross'] = '0.49' Payment.process_paypal_ipn(relatively_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) bad_form = good_form relatively_bad_form['txn_id'] = 'TEST3' bad_form['business'] = current_app.config['PAYPAL_BUSINESS'] Payment.process_paypal_ipn(bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1) super_bad_form = good_form relatively_bad_form['txn_id'] = 'TEST4' super_bad_form['payment_status'] = 'Refunded' # What kind of monster would do that?! Payment.process_paypal_ipn(super_bad_form) # There should still be one recorded donation self.assertEqual(len(Payment.query.all()), 1)
def test_verify_and_log_wepay_checkout_donation(self): checkout_id = 12345 self.assertTrue( Payment.verify_and_log_wepay_checkout( checkout_id=12345, is_donation=True, editor='Tester', anonymous=False, can_contact=True, )) # Donation should be in the DB now self.assertEqual(Payment.query.all()[0].transaction_id, str(checkout_id))
def test_currency_euro(self): form = copy.deepcopy(self.base_form) form["mc_currency"] = "eur" Payment.process_paypal_ipn(form) payments = Payment.query.all() self.assertEqual(payments[0].currency, Currency.Euro.value)
def test_log_stripe_charge_payment(self): # Function should execute without any exceptions charge = convert_to_stripe_object( { "id": u"ch_15AjX1F21qH57QtHT6avvqrM", "object": u"charge", "created": 1418829367, "livemode": False, "paid": True, "status": u"succeeded", "amount": 99999900, "currency": u"usd", "refunded": False, "source": { "id": u"card_15AjWxF21qH57QtHHVNgaHOP", "object": u"card", "last4": u"4242", "brand": u"Visa", "funding": u"credit", "exp_month": 11, "exp_year": 2016, "country": u"US", "name": u"Uh Oh", "address_line1": u"test 12", "address_line2": None, "address_city": u"Schenectady", "address_state": u"NY", "address_zip": u"12345", "address_country": u"United States", "cvc_check": u"pass", "address_line1_check": u"pass", "address_zip_check": u"pass", "dynamic_last4": None, "metadata": {}, "customer": None }, "captured": True, "balance_transaction": u"txn_159qthF21qH57QtHBksXX3tN", "failure_message": None, "failure_code": None, "amount_refunded": 0, "customer": None, "invoice": None, "description": u"Donation to MetaBrainz Foundation", "dispute": None, "metadata": { "is_donation": False, "email": u"*****@*****.**", "invoice_number": 42, }, "statement_descriptor": None, "fraud_details": {}, "receipt_email": None, "receipt_number": None, "shipping": None, "application_fee": None, "refunds": { "object": "list", "total_count": 0, "has_more": False, "url": "/v1/charges/ch_15AjX1F21qH57QtHT6avvqrM/refunds", "data": [] } }, api_key=None, account=None) Payment.log_stripe_charge(charge)
def nag_check(editor): a, b = Payment.get_nag_days(editor) return '%s,%s\n' % (a, b)
def test_log_stripe_charge_payment(self): # Function should execute without any exceptions charge = convert_to_stripe_object( { "id": u"ch_15AjX1F21qH57QtHT6avvqrM", "object": u"charge", "created": 1418829367, "livemode": False, "paid": True, "status": u"succeeded", "amount": 99999900, "currency": u"usd", "refunded": False, "source": { "id": u"card_15AjWxF21qH57QtHHVNgaHOP", "object": u"card", "last4": u"4242", "brand": u"Visa", "funding": u"credit", "exp_month": 11, "exp_year": 2016, "country": u"US", "name": u"Uh Oh", "address_line1": u"test 12", "address_line2": None, "address_city": u"Schenectady", "address_state": u"NY", "address_zip": u"12345", "address_country": u"United States", "cvc_check": u"pass", "address_line1_check": u"pass", "address_zip_check": u"pass", "dynamic_last4": None, "metadata": {}, "customer": None }, "captured": True, "balance_transaction": u"txn_159qthF21qH57QtHBksXX3tN", "failure_message": None, "failure_code": None, "amount_refunded": 0, "customer": None, "invoice": None, "description": u"Donation to MetaBrainz Foundation", "dispute": None, "metadata": { "is_donation": False, "email": u"*****@*****.**", "invoice_number": 42, }, "statement_descriptor": None, "fraud_details": {}, "receipt_email": None, "receipt_number": None, "shipping": None, "application_fee": None, "refunds": { "object": "list", "total_count": 0, "has_more": False, "url": "/v1/charges/ch_15AjX1F21qH57QtHT6avvqrM/refunds", "data": [] } }, api_key=None, account=None ) Payment.log_stripe_charge(charge)