Support for paypal subscription

This commit is contained in:
Saurabh 2018-08-30 19:25:42 +05:30
parent 2749f0ebbf
commit d20fd4af65
2 changed files with 125 additions and 21 deletions

View file

@ -28,7 +28,14 @@ Example:
"payer_name": "Nuran Verkleij",
"order_id": "111",
"currency": "USD",
"payment_gateway": "Razorpay"
"payment_gateway": "Razorpay",
"subscription_details": {
"plan_id": "plan_12313", # if Required
"start_date": "2018-08-30",
"billing_period": "Month" #(Day, Week, SemiMonth, Month, Year),
"billing_frequency": 1,
"customer_notify": 1
}
}
# redirect the user to this url
@ -59,7 +66,8 @@ from __future__ import unicode_literals
import frappe
import json
from frappe import _
from frappe.utils import get_url, call_hook_method, cint
from datetime import datetime
from frappe.utils import get_url, call_hook_method, cint, get_timestamp
from six.moves.urllib.parse import urlencode
from frappe.model.document import Document
from frappe.integrations.utils import create_request_log, make_post_request, create_payment_gateway
@ -124,7 +132,7 @@ class PayPalSettings(Document):
def get_payment_url(self, **kwargs):
setattr(self, "use_sandbox", cint(kwargs.get("use_sandbox", 0)))
response = self.execute_set_express_checkout(kwargs["amount"], kwargs["currency"])
response = self.execute_set_express_checkout(**kwargs)
if self.paypal_sandbox or self.use_sandbox:
return_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}"
@ -135,21 +143,31 @@ class PayPalSettings(Document):
"token": response.get("TOKEN")[0],
"correlation_id": response.get("CORRELATIONID")[0]
})
print(kwargs)
self.integration_request = create_request_log(kwargs, "Remote", "PayPal", response.get("TOKEN")[0])
print(return_url.format(kwargs["token"]))
return return_url.format(kwargs["token"])
def execute_set_express_checkout(self, amount, currency):
def execute_set_express_checkout(self, **kwargs):
params, url = self.get_paypal_params_and_url()
params.update({
"METHOD": "SetExpressCheckout",
"PAYMENTREQUEST_0_PAYMENTACTION": "SALE",
"PAYMENTREQUEST_0_AMT": amount,
"PAYMENTREQUEST_0_CURRENCYCODE": currency.upper(),
"returnUrl": get_url("/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings.get_express_checkout_details"),
"cancelUrl": get_url("/payment-cancel")
})
if kwargs.get('subscription_details'):
params.update({
"L_BILLINGTYPE0": "RecurringPayments", #The type of billing agreement
"L_BILLINGAGREEMENTDESCRIPTION0": kwargs['description']
})
else:
params.update({
"PAYMENTREQUEST_0_PAYMENTACTION": "SALE",
"PAYMENTREQUEST_0_AMT": kwargs['amount'],
"PAYMENTREQUEST_0_CURRENCYCODE": kwargs['currency'].upper(),
})
params = urlencode(params)
@ -181,14 +199,16 @@ def get_express_checkout_details(token):
return
doc = frappe.get_doc("Integration Request", token)
update_integration_request_status(token, {
"payerid": response.get("PAYERID")[0],
"payer_email": response.get("EMAIL")[0]
}, "Authorized")
}, "Authorized", doc=doc)
frappe.local.response["type"] = "redirect"
frappe.local.response["location"] = get_url( \
"/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings.confirm_payment?token={0}".format(token))
frappe.local.response["location"] = get_redirect_uri(doc, token, response.get("PAYERID")[0])
except Exception:
frappe.log_error(frappe.get_traceback())
@ -251,5 +271,88 @@ def confirm_payment(token):
except Exception:
frappe.log_error(frappe.get_traceback())
def update_integration_request_status(token, data, status, error=False):
frappe.get_doc("Integration Request", token).update_status(data, status)
@frappe.whitelist(allow_guest=True, xss_safe=True)
def create_recurring_profile(token, payerid):
try:
redirect = True
status_changed_to, redirect_to = None, None
doc = frappe.get_doc("PayPal Settings")
doc.setup_sandbox_env(token)
integration_request = frappe.get_doc("Integration Request", token)
data = json.loads(integration_request.data)
subscription_details = data.get("subscription_details")
redirect_to = data.get('redirect_to') or None
redirect_message = data.get('redirect_message') or None
params, url = doc.get_paypal_params_and_url()
params.update({
"METHOD": "CreateRecurringPaymentsProfile",
"PAYERID": payerid,
"TOKEN": token,
"DESC": data.get("description"),
"BILLINGPERIOD": subscription_details.get("billing_period"),
"BILLINGFREQUENCY": subscription_details.get("billing_frequency"),
"AMT": data.get("amount"),
"CURRENCYCODE": data.get("currency").upper()
})
if subscription_details.get("start_date"):
starts_at = subscription_details.get("start_date")
else:
starts_at = frappe.utils.now()
params.update({
"PROFILESTARTDATE": datetime.utcfromtimestamp(get_timestamp(starts_at)).isoformat()
})
response = make_post_request(url, data=params)
if response.get("ACK")[0] == "Success":
update_integration_request_status(token, {
"profile_id": response.get("PROFILEID")[0],
}, "Completed")
if data.get("reference_doctype") and data.get("reference_docname"):
frappe.flags.data = data
custom_redirect_to = frappe.get_doc(data.get("reference_doctype"),
data.get("reference_docname")).run_method("on_payment_authorized", "Completed")
frappe.db.commit()
if custom_redirect_to:
redirect_to = custom_redirect_to
redirect_url = '/integrations/payment-success'
else:
redirect_url = "/integrations/payment-failed"
if redirect_to:
redirect_url += '?' + urlencode({'redirect_to': redirect_to})
if redirect_message:
redirect_url += '&' + urlencode({'redirect_message': redirect_message})
# this is done so that functions called via hooks can update flags.redirect_to
if redirect:
frappe.local.response["type"] = "redirect"
frappe.local.response["location"] = get_url(redirect_url)
except Exception:
frappe.log_error(frappe.get_traceback())
def update_integration_request_status(token, data, status, error=False, doc=None):
if not doc:
doc = frappe.get_doc("Integration Request", token)
doc.update_status(data, status)
def get_redirect_uri(doc, token, payerid):
data = json.loads(doc.data)
if data.get("subscription_details"):
return get_url( \
"/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings.create_recurring_profile?token={0}&payerid={1}".format(token, payerid))
else:
return get_url( \
"/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings.confirm_payment?token={0}".format(token))

View file

@ -4,9 +4,9 @@
from __future__ import unicode_literals
import frappe
import json
import json,datetime
from six.moves.urllib.parse import parse_qs
from six import string_types
from six import string_types, text_type
from frappe.utils import get_request_session
from frappe import _
@ -40,6 +40,7 @@ def make_post_request(url, auth=None, headers=None, data=None):
s = get_request_session()
frappe.flags.integration_request = s.post(url, data=data, auth=auth, headers=headers)
frappe.flags.integration_request.raise_for_status()
print(frappe.flags.integration_request.text)
if frappe.flags.integration_request.headers.get("content-type") == "text/plain; charset=utf-8":
return parse_qs(frappe.flags.integration_request.text)
@ -59,7 +60,7 @@ def create_request_log(data, integration_type, service_name, name=None):
"integration_request_service": service_name,
"reference_doctype": data.get("reference_doctype"),
"reference_docname": data.get("reference_docname"),
"data": json.dumps(data)
"data": json.dumps(data, default=json_handler)
})
if name:
@ -90,10 +91,6 @@ def get_checkout_url(**kwargs):
try:
if kwargs.get('payment_gateway'):
doc = frappe.get_doc("{0} Settings".format(kwargs.get('payment_gateway')))
resp = doc.run_method("before_get_payment_url", **kwargs)
if isinstance(resp, dict):
kwargs.update(resp)
return doc.get_payment_url(**kwargs)
else:
raise Exception
@ -113,3 +110,7 @@ def create_payment_gateway(gateway, settings=None, controller=None):
"gateway_controller": controller
})
payment_gateway.insert(ignore_permissions=True)
def json_handler(obj):
if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)):
return text_type(obj)