You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

222 lines
6.9 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  3. # License: GNU General Public License v3. See license.txt
  4. """
  5. # Integrating RazorPay
  6. ### Validate Currency
  7. Example:
  8. from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller
  9. controller = get_integration_controller("Razorpay")
  10. controller().validate_transaction_currency(currency)
  11. ### 2. Redirect for payment
  12. Example:
  13. payment_details = {
  14. "amount": 600,
  15. "title": "Payment for bill : 111",
  16. "description": "payment via cart",
  17. "reference_doctype": "Payment Request",
  18. "reference_docname": "PR0001",
  19. "payer_email": "NuranVerkleij@example.com",
  20. "payer_name": "Nuran Verkleij",
  21. "order_id": "111",
  22. "currency": "INR"
  23. }
  24. # Redirect the user to this url
  25. url = controller().get_payment_url(**payment_details)
  26. ### 3. On Completion of Payment
  27. Write a method for `on_payment_authorized` in the reference doctype
  28. Example:
  29. def on_payment_authorized(payment_status):
  30. # this method will be called when payment is complete
  31. ##### Notes:
  32. payment_status - payment gateway will put payment status on callback.
  33. For razorpay payment status is Authorized
  34. """
  35. from __future__ import unicode_literals
  36. import frappe
  37. from frappe import _
  38. import urllib, json
  39. from frappe.utils import get_url, call_hook_method
  40. from frappe.integration_broker.integration_controller import IntegrationController
  41. class Controller(IntegrationController):
  42. service_name = 'Razorpay'
  43. parameters_template = [
  44. {
  45. "label": "API Key",
  46. 'fieldname': 'api_key',
  47. 'fieldtype': "Data",
  48. 'reqd': 1
  49. },
  50. {
  51. "label": "API Secret",
  52. 'fieldname': 'api_secret',
  53. 'fieldtype': "Password",
  54. 'reqd': 1
  55. }
  56. ]
  57. # do also changes in razorpay.js scheduler job helper
  58. scheduled_jobs = [
  59. {
  60. "all": [
  61. "frappe.integrations.razorpay.capture_payment"
  62. ]
  63. }
  64. ]
  65. js = "assets/frappe/js/integrations/razorpay.js"
  66. supported_currencies = ["INR"]
  67. def enable(self, parameters, use_test_account=0):
  68. call_hook_method('payment_gateway_enabled', gateway='Razorpay')
  69. self.parameters = parameters
  70. self.validate_razorpay_credentails()
  71. def validate_razorpay_credentails(self):
  72. razorpay_settings = self.get_settings()
  73. if razorpay_settings.get("api_key"):
  74. try:
  75. self.get_request(url="https://api.razorpay.com/v1/payments",
  76. auth=(razorpay_settings.api_key, razorpay_settings.api_secret))
  77. except Exception:
  78. frappe.throw(_("Seems API Key or API Secret is wrong !!!"))
  79. def validate_transaction_currency(self, currency):
  80. if currency not in self.supported_currencies:
  81. frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency))
  82. def get_payment_url(self, **kwargs):
  83. return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs)))
  84. def get_settings(self):
  85. if hasattr(self, "parameters"):
  86. return frappe._dict(self.parameters)
  87. custom_settings_json = frappe.db.get_value("Integration Service", "Razorpay", "custom_settings_json")
  88. if custom_settings_json:
  89. return frappe._dict(json.loads(custom_settings_json))
  90. def create_request(self, data):
  91. self.data = frappe._dict(data)
  92. try:
  93. self.integration_request = super(Controller, self).create_request(self.data, "Host", \
  94. self.service_name)
  95. return self.authorize_payment()
  96. except Exception:
  97. frappe.log_error(frappe.get_traceback())
  98. return{
  99. "redirect_to": frappe.redirect_to_message(_('Server Error'), _("Seems issue with server's razorpay config. Don't worry, in case of failure amount will get refunded to your account.")),
  100. "status": 401
  101. }
  102. def authorize_payment(self):
  103. """
  104. An authorization is performed when user’s payment details are successfully authenticated by the bank.
  105. The money is deducted from the customer’s account, but will not be transferred to the merchant’s account
  106. until it is explicitly captured by merchant.
  107. """
  108. settings = self.get_settings()
  109. data = json.loads(self.integration_request.data)
  110. redirect_to = data.get('notes', {}).get('redirect_to') or None
  111. redirect_message = data.get('notes', {}).get('redirect_message') or None
  112. if self.integration_request.status != "Authorized":
  113. try:
  114. resp = self.get_request("https://api.razorpay.com/v1/payments/{0}"
  115. .format(self.data.razorpay_payment_id), auth=(settings.api_key,
  116. settings.api_secret))
  117. if resp.get("status") == "authorized":
  118. self.integration_request.db_set('status', 'Authorized', update_modified=False)
  119. self.flags.status_changed_to = "Authorized"
  120. except:
  121. frappe.log_error(frappe.get_traceback())
  122. # failed
  123. pass
  124. status = frappe.flags.integration_request.status_code
  125. if self.flags.status_changed_to == "Authorized":
  126. if self.data.reference_doctype and self.data.reference_docname:
  127. custom_redirect_to = frappe.get_doc(self.data.reference_doctype,
  128. self.data.reference_docname).run_method("on_payment_authorized", self.flags.status_changed_to)
  129. if custom_redirect_to:
  130. redirect_to = custom_redirect_to
  131. redirect_url = 'payment-success'
  132. else:
  133. redirect_url = 'payment-failed'
  134. if redirect_to:
  135. redirect_url += '?' + urllib.urlencode({'redirect_to': redirect_to})
  136. if redirect_message:
  137. redirect_url += '&' + urllib.urlencode({'redirect_message': redirect_message})
  138. return {
  139. "redirect_to": redirect_url,
  140. "status": status
  141. }
  142. def capture_payment(is_sandbox=False, sanbox_response=None):
  143. """
  144. Verifies the purchase as complete by the merchant.
  145. After capture, the amount is transferred to the merchant within T+3 days
  146. where T is the day on which payment is captured.
  147. Note: Attempting to capture a payment whose status is not authorized will produce an error.
  148. """
  149. controller = frappe.get_doc("Integration Service", "Razorpay")
  150. settings = controller.get_parameters()
  151. for doc in frappe.get_all("Integration Request", filters={"status": "Authorized",
  152. "integration_request_service": "Razorpay"}, fields=["name", "data"]):
  153. try:
  154. if is_sandbox:
  155. resp = sanbox_response
  156. else:
  157. data = json.loads(doc.data)
  158. resp = controller.post_request("https://api.razorpay.com/v1/payments/{0}/capture".format(data.get("razorpay_payment_id")),
  159. auth=(settings["api_key"], settings["api_secret"]), data={"amount": data.get("amount")})
  160. if resp.get("status") == "captured":
  161. frappe.db.set_value("Integration Request", doc.name, "status", "Completed")
  162. except Exception:
  163. doc = frappe.get_doc("Integration Request", doc.name)
  164. doc.status = "Failed"
  165. doc.error = frappe.get_traceback()
  166. @frappe.whitelist(allow_guest=True, xss_safe=True)
  167. def get_checkout_url(**kwargs):
  168. try:
  169. return Controller().get_payment_url(**kwargs)
  170. except Exception:
  171. frappe.respond_as_web_page(_("Something went wrong"),
  172. _("Looks like something is wrong with this site's Razorpay configuration. Don't worry! No payment has been made."),
  173. success=False,
  174. http_status_code=frappe.ValidationError.http_status_code)