diff --git a/mhackspace/subscriptions/payments.py b/mhackspace/subscriptions/payments.py new file mode 100644 index 0000000..c9c9003 --- /dev/null +++ b/mhackspace/subscriptions/payments.py @@ -0,0 +1,425 @@ +from pprint import pprint +import pytz +import gocardless +import braintree +# import gocardless_pro +import paypalrestsdk as paypal + +from website.config import settings +from website.config.settings import app_domain + +from website.config.logger import log + +PROVIDER_ID = {'gocardless':1, 'paypal': 2} +PROVIDER_NAME = {1: 'gocardless', 2: 'paypal'} + +payment_providers = { + 'paypal': { + "mode": "sandbox", # sandbox or live + 'credentials': { + "mode": "sandbox", # sandbox or live + "client_id": "AaGlNEvd26FiEJiJi53nfpXh19_oKetteV1NGkBi4DDYZSqBexKVgaz9Lp0SI82gYFSAYpsmxO4iDtxU", + "client_secret": "EMcIuDJE_VDNSNZS7C7NLi9DEHaDvVu9jlIYyCCHaLmrLuy_VQ6C0bbcRnyF-7B6CcN__Dn6HqUwsgMG"} + }, + 'gocardless':{ + 'environment': 'sandbox', + 'credentials': { + 'app_id': 'MNHBS3C4X4ZG211SM70WSS7WCN8B3X1KAWZBKV9S8N6KH2RNH6YZ5Z5865RFD3H6', + 'app_secret': 'NE4NWYDQY4FNN1B47VT9SZ318GPQND130DW7QGQ73JMVTZQZHJQNF23ZFNP48GKV', + 'access_token': 'CJ7G7V36VAH5KVAHTYXD8VE8M4M0S41EQXH2E1HTGV5AN5TAZBER36ERAF4CG2NR', + 'merchant_id': '11QFXD7TTA', + }, + 'redirect_url':'https://test.maidstone-hackspace.org.uk' + } +} + + +def select_provider(type): + if type == "gocardless": return gocardless_provider() + if type == "braintree": return braintree_provider() + if type == "paypal": return paypal_provider() + + log.exception('[scaffold] - "No Provider for ' + type) + assert 0, "No Provider for " + type + +class gocardless_provider: + form_remote = True + client = None + + def __init__(self): + # gocardless are changing there api, not sure if we can switch yet + # self.client = gocardless_pro.Client( + # access_token=settings.payment_providers['gocardless']['credentials']['access_token'], + # environment=settings.payment_providers['gocardless']['environment']) + + print(settings.payment_providers.keys) + gocardless.environment = settings.payment_providers['gocardless']['environment'] + gocardless.set_details(**settings.payment_providers['gocardless']['credentials']) + self.client = gocardless.client.merchant() + + def subscribe_confirm(self, args): + response = gocardless.client.confirm_resource(args) + subscription = gocardless.client.subscription(args.get('resource_id')) + return { + 'amount': subscription.amount, + 'start_date': subscription.created_at, + 'reference': subscription.id + } + + def fetch_subscriptions(self): + for paying_member in self.client.subscriptions(): + user=paying_member.user() + # for bill in paying_member.bills(): + # print('test') + # print(dir(bill)) + # print(bill.created_at) + print(dir(paying_member)) + print(paying_member.reference_fields) + yield { + 'status': paying_member.status, + 'email': user.email, + 'start_date': paying_member.created_at, + 'reference': paying_member.id, + 'amount': paying_member.amount} + + def get_redirect_url(self): + return settings.payment_providers['gocardless']['redirect_url'] + + def get_token(self): + return 'N/A' + + def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'): + return gocardless.client.new_subscription_url( + amount=float(amount), + interval_length=interval_length, + interval_unit=interval_unit, + name=name, + redirect_uri=redirect_success) + + +class braintree_provider: + form_remote = False + + def __init__(self): + braintree.Configuration.configure( + environment=braintree.Environment.Sandbox, + merchant_id=settings.payment_providers['braintree']['credentials']['merchant_id'], + public_key=settings.payment_providers['braintree']['credentials']['public_key'], + private_key=settings.payment_providers['braintree']['credentials']['private_key']) + + def get_token(self): + return braintree.ClientToken.generate() + + + def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'): + result = braintree.Customer.create({ + "first_name": "test", + "last_name": "user", + "payment_method_nonce": nonce_from_the_client + }) + + return braintree.Subscription.create({ + "payment_method_token": "the_token", + "plan_id": "membership", + "merchant_account_id": "gbp_sandbox" + #"price": "20.00" + #'name': name + }) + + def fetch_subscriptions(self): + for paying_member in braintree.Subscription.search(braintree.SubscriptionSearch.status == braintree.Subscription.Status.Active): + user=paying_member.user() + yield { + 'status': paying_member.status, + 'email': user.email, + 'start_date': paying_member.created_at, + 'reference': paying_member.reference, + 'amount': paying_member.amount} + + +class payment: + """ + paypal reference = https://github.com/paypal/PayPal-Python-SDK + gocardless reference = https://github.com/paypal/PayPal-Python-SDK + """ + #~ def __call__(self, **args): + #~ return self + + def __init__(self, provider='gocardless', style='payment', mode='sandbox'): + self.provider = provider + self.environment = int(mode=='production') + self.provider_id = PROVIDER_ID.get(provider) + + print(settings.payment_providers) + if provider == 'paypal': + paypal.configure(**settings.payment_providers[provider]['credentials']) + return + + gocardless_pro.Client( + access_token=settings.payment_providers[provider]['credentials']['access_token'], + environment=settings.payment_providers[provider]) + #~ environment = int('production' = settings.payment_providers[provider]['environment']) + gocardless.environment = settings.payment_providers[provider]['environment'] + gocardless.set_details(**settings.payment_providers[provider]['credentials']) + merchant = gocardless.client.merchant() + + def lookup_provider_by_id(self, provider_id): + return PROVIDER_NAME.get(provider_id, None) + + def make_donation(self, amount, reference, redirect_success, redirect_failure): + if self.provider == 'paypal': + payment = paypal.Payment({ + "intent": "sale", + "payer": {"payment_method": "paypal"}, + "redirect_urls": { + "return_url": redirect_success, + "cancel_url": redirect_failure}, + + "transactions": [{ + "amount": { + "total": amount, + "currency": "GBP"}, + "description": reference}]}) + + payment_response = payment.create() + print('payment create') + if payment_response: + print(payment_response) + for link in payment.links: + if link.method == "REDIRECT": + redirect_url = str(link.href) + print(redirect_url) + return str(redirect_url) + else: + print("Error while creating payment:") + print(payment.error) + + if self.provider == 'gocardless': + return gocardless.client.new_bill_url( + float(amount), + name=reference, + redirect_uri=redirect_success) + + return 'Error something went wrong' + + def fetch_subscriptions(self): + if self.provider == 'gocardless': + merchant = gocardless.client.merchant() + for paying_member in merchant.subscriptions(): + user=paying_member.user() + print(dir(paying_member)) + print(paying_member.next_interval_start) + print(paying_member.status) + print(dir(paying_member.user())) + yield { + 'email': user.email, + 'start_date': paying_member.created_at, + 'reference': paying_member.id, + 'amount': paying_member.amount} + + if self.provider == 'paypal': + #~ I-S39170DK26AF + #~ start_date, end_date = "2014-07-01", "2014-07-20" + billing_agreement = paypal.BillingAgreement.find('') + print(billing_agreement) + print(dir(billing_agreement)) + #~ print billing_agreement.search_transactions(start_date, end_date) + #~ transactions = billing_agreement.search_transactions(start_date, end_date) + payment_history = paypal.Payment.all({"count": 2}) + + # List Payments + print("List Payment:") + print(payment_history) + for payment in payment_history.payments: + print(" -> Payment[%s]" % (payment.id)) + #~ print paypal.BillingAgreement.all() + history = paypal.BillingPlan.all( + {"status": "CREATED", "page_size": 5, "page": 1, "total_required": "yes"}) + print(history) + + print("List BillingPlan:") + for plan in history.plans: + print(dir(plan)) + print(plan.to_dict()) + print(" -> BillingPlan[%s]" % (plan.id)) + + #~ merchant = gocardless.client.merchant() + #~ for paying_member in merchant.subscriptions(): + #~ user=paying_member.user() + #~ yield { + #~ 'email': user.email, + #~ 'start_date': paying_member.created_at, + #~ 'reference': paying_member.id, + #~ 'amount': paying_member.amount} + + def subscribe_confirm(self, args): + if self.provider == 'gocardless': + response = gocardless.client.confirm_resource(args) + subscription = gocardless.client.subscription(args.get('resource_id')) + return { + 'amount': subscription.amount, + 'start_date': subscription.created_at, + 'reference': subscription.id + } + + if self.provider == 'paypal': + print('subscribe_confirm') + payment_token = args.get('token', '') + billing_agreement_response = paypal.BillingAgreement.execute(payment_token) + amount = 0 + print(billing_agreement_response) + print(billing_agreement_response.id) + for row in billing_agreement_response.plan.payment_definitions: + amount = row.amount.value + + return { + 'amount': amount, + 'start_date': billing_agreement_response.start_date, + 'reference': billing_agreement_response.id + } + + return None + + def unsubscribe(self, reference): + if self.provider == 'gocardless': + print('unsubscribe gocardless') + subscription = gocardless.client.subscription(reference) + print(subscription.cancel()) + + if self.provider == 'paypal': + # this may be wrong + # ManageRecurringPaymentsProfileStatus + print(reference) + billing_plan = paypal.BillingAgreement.find(reference) + print(billing_plan) + print(billing_plan.error) + #~ billing_plan.replace([{"op": "replace","path": "/","value": {"state":"DELETED"}}]) + print(billing_plan.error) + #~ invoice = paypal.Invoice.find(reference) + options = { + "subject": "Cancelling membership", + "note": "Canceling invoice", + "send_to_merchant": True, + "send_to_payer": True + } + + if billing_plan.cancel(options): # return True or False + print("Invoice[%s] cancel successfully" % (invoice.id)) + else: + print(billing_plan.error) + + + def subscribe(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'): + if self.provider == 'gocardless': + return gocardless.client.new_subscription_url( + amount=float(amount), + interval_length=interval_length, + interval_unit=interval_unit, + name=name, + redirect_uri=redirect_success) + + if self.provider == 'paypal': + billing_plan = paypal.BillingPlan({ + "name": name, + "description": "Membership subscription", + "merchant_preferences": { + "auto_bill_amount": "yes", + "cancel_url": redirect_failure, + "initial_fail_amount_action": "continue", + "max_fail_attempts": "1", + "return_url": redirect_success, + "setup_fee": { + "currency": "GBP", + "value": amount + } + }, + "payment_definitions": [{ + "amount": { + "currency": "GBP", + "value": amount + }, + "cycles": "0", + "frequency": interval_unit, + "frequency_interval": interval_length, + "name": "Regular 1", + "type": "REGULAR" + } + ], + "type": "INFINITE" + }) + print('create bill') + + response = billing_plan.create() + + billing_plan = paypal.BillingPlan.find(billing_plan.id) + + if billing_plan.activate(): + start_date = datetime.utcnow() + timedelta(minutes=10) + billing_agreement = paypal.BillingAgreement({ + "name": billing_plan.name, + "description": name, + "start_date": start_date.strftime('%Y-%m-%dT%H:%M:%SZ'), + "plan": {"id": str(billing_plan.id)}, + "payer": {"payment_method": "paypal"} + }) + + if billing_agreement.create(): + print('billing agreement id') + print(billing_agreement.id) + + for link in billing_agreement.links: + if link.rel == "approval_url": + approval_url = link.href + return approval_url + else: + print(billing_agreement.error) + print('failed') + + def confirm(self, args): + confirm_details = {} + confirm_details['successfull'] = False + print('---------------------') + print(args) + + + + from pprint import pprint + if self.provider == 'paypal': + print(args.get('paymentId')) + print(args.get('PayerID')) + payment = paypal.Payment.find(args.get('paymentId')) + pprint(payment) + print(pprint(payment)) + print(payment) + + confirm_details['name'] = payment['payer']['payer_info'].first_name + ' ' + payment['payer']['payer_info'].last_name + confirm_details['user'] = payment['payer']['payer_info'].email + confirm_details['status'] = payment.state + confirm_details['amount'] = payment['transactions'][0]['amount'].total + confirm_details['created'] = payment.create_time + confirm_details['reference'] = payment.id + pprint(confirm_details) + + + if payment.execute({"payer_id": args.get('PayerID')}): # return True or False + confirm_details['successfull'] = True + print("Payment[%s] execute successfully" % (args.get('paymentId'))) + else: + print(payment.error) + return confirm_details + + if self.provider == 'gocardless': + bill_id = args.get('resource_id') + gocardless.client.confirm_resource(args) + if bill_id: + bill = gocardless.client.bill(bill_id) + confirm_details['name'] = bill.name + confirm_details['user'] = bill.user + confirm_details['status'] = bill.status + confirm_details['amount'] = bill.amount + #~ confirm_details['amount_minus_fees'] = bill.amount_minus_fees + confirm_details['created'] = bill.created_at + confirm_details['reference'] = bill_id + confirm_details['successfull'] = True + return confirm_details + return None diff --git a/mhackspace/subscriptions/tests/test_payment_gateways.py b/mhackspace/subscriptions/tests/test_payment_gateways.py new file mode 100644 index 0000000..818b030 --- /dev/null +++ b/mhackspace/subscriptions/tests/test_payment_gateways.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import unittest +from mock import patch, Mock + +from website.libs.payments import payment, gocardless_provider, braintree_provider + +class TestPaymentGatewaysGocardless(unittest.TestCase): + + def setUp(self): + self.auth_gocardless() + + @patch('website.libs.payments.gocardless.request.requests.get', autospec=True) + def auth_gocardless(self, mock_request): + # mock braintree initalisation request + mock_request.return_value = Mock(ok=True) + mock_request.return_value.json.return_value = { + "id": "1", + "created_at": "2011-11-18T17:07:09Z", + "access_token": "test_token", + "next_payout_date": "2011-11-18T17:07:09Z" + } + + with patch('gocardless.resources.Merchant') as mock_subscription: + self.provider = gocardless_provider() + return self.provider #self.provider + + def test_confirm_subscription_callback(self): + with patch('gocardless.client.confirm_resources') as mock_subscription: + self.provider = gocardless_provider() + + def test_fetch_subscription_gocardless(self): + items = [Mock( + id='01', + status='active', + amount=20.00, + reference='ref01', + created_at='date' + )] + items[-1].user.return_value = Mock(email='test@test.com') + + self.provider.client = Mock() + self.provider.client.subscriptions = Mock(return_value=items) + for item in self.provider.fetch_subscriptions(): + self.assertEqual(item.get('status'), 'active') + self.assertEqual(item.get('email'), 'test@test.com') + self.assertEqual(item.get('reference'), 'ref01') + self.assertEqual(item.get('start_date'), 'date') + self.assertEqual(item.get('amount'), 20.00) + + +class TestPaymentGatewaysBraintree(unittest.TestCase): + @patch('website.libs.payments.braintree.Configuration.configure') + def auth_braintree(self, mock_request): + # mock braintree initalisation request + mock_request.return_value = Mock(ok=True) + mock_request.return_value.json.return_value = { + "id": "1", + "created_at": "2011-11-18T17:07:09Z", + "access_token": "test_token", + "next_payout_date": "2011-11-18T17:07:09Z" + } + + self.provider = braintree_provider() + + @patch('website.libs.payments.braintree.Subscription.search') + def test_fetch_subscription_braintree(self, mock_request): + provider = self.auth_braintree() + + items = [Mock( + id='01', + status='active', + amount=20.00, + reference='ref01', + created_at='date' + )] + items[-1].user.return_value = Mock(email='test@test.com') + + mock_request.return_value = items + for item in self.provider.fetch_subscriptions(): + self.assertEqual(item.get('status'), 'active') + self.assertEqual(item.get('email'), 'test@test.com') + self.assertEqual(item.get('reference'), 'ref01') + self.assertEqual(item.get('start_date'), 'date') + self.assertEqual(item.get('amount'), 20.00)