diff --git a/payments/payment_gateways/doctype/stripe_settings/__init__.py b/payments/payment_gateways/doctype/stripe_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js new file mode 100644 index 0000000..578ae94 --- /dev/null +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Stripe Settings', { + refresh: function(frm) { + + } +}); diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.json b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.json new file mode 100644 index 0000000..adf8a82 --- /dev/null +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.json @@ -0,0 +1,120 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-03-09 17:18:29.458397", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "publishable_key", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Publishable Key", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "secret_key", + "fieldtype": "Password", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Secret Key", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2017-03-09 17:19:25.087475", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Stripe Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py new file mode 100644 index 0000000..9966ff1 --- /dev/null +++ b/payments/payment_gateways/doctype/stripe_settings/stripe_settings.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from frappe import _ +import urllib +from frappe.utils import get_url, call_hook_method, cint +from frappe.integrations.utils import make_get_request, make_post_request, create_request_log, create_payment_gateway + +class StripeSettings(Document): + supported_currencies = [ + "AED", "ALL", "ANG", "ARS", "AUD", "AWG", "BBD", "BDT", "BIF", "BMD", "BND", + "BOB", "BRL", "BSD", "BWP", "BZD", "CAD", "CHF", "CLP", "CNY", "COP", "CRC", "CVE", "CZK", "DJF", + "DKK", "DOP", "DZD", "EGP", "ETB", "EUR", "FJD", "FKP", "GBP", "GIP", "GMD", "GNF", "GTQ", "GYD", + "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "INR", "ISK", "JMD", "JPY", "KES", "KHR", "KMF", + "KRW", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "MAD", "MDL", "MNT", "MOP", "MRO", "MUR", "MVR", + "MWK", "MXN", "MYR", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "PAB", "PEN", "PGK", "PHP", "PKR", + "PLN", "PYG", "QAR", "RUB", "SAR", "SBD", "SCR", "SEK", "SGD", "SHP", "SLL", "SOS", "STD", "SVC", + "SZL", "THB", "TOP", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VND", "VUV", "WST", + "XAF", "XOF", "XPF", "YER", "ZAR" + ] + + def validate(self): + create_payment_gateway('Stripe') + call_hook_method('payment_gateway_enabled', gateway='Stripe') + if not self.flags.ignore_mandatory: + self.validate_stripe_credentails() + + def validate_stripe_credentails(self): + if self.publishable_key and self.secret_key: + header = {"Authorization": "Bearer {0}".format(self.get_password(fieldname="secret_key", raise_exception=False))} + try: + make_get_request(url="https://api.stripe.com/v1/charges", headers=header) + except Exception: + frappe.throw(_("Seems Publishable Key or Secret Key is wrong !!!")) + + def validate_transaction_currency(self, currency): + if currency not in self.supported_currencies: + frappe.throw(_("Please select another payment method. Stripe does not support transactions in currency '{0}'").format(currency)) + + def get_payment_url(self, **kwargs): + return get_url("./integrations/stripe_checkout?{0}".format(urllib.urlencode(kwargs))) + + def create_request(self, data): + self.data = frappe._dict(data) + + try: + self.integration_request = create_request_log(self.data, "Host", "Stripe") + return self.create_charge_on_stripe() + except Exception: + frappe.log_error(frappe.get_traceback()) + return{ + "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.")), + "status": 401 + } + + def create_charge_on_stripe(self): + headers = {"Authorization": + "Bearer {0}".format(self.get_password(fieldname="secret_key", raise_exception=False))} + + data = { + "amount": cint(self.data.amount)*100, + "currency": self.data.currency, + "source": self.data.stripe_token_id, + "description": self.data.description + } + + redirect_to = self.data.get('redirect_to') or None + redirect_message = self.data.get('redirect_message') or None + + try: + resp = make_post_request(url="https://api.stripe.com/v1/charges", headers=headers, data=data) + + if resp.get("captured") == True: + self.integration_request.db_set('status', 'Completed', update_modified=False) + self.flags.status_changed_to = "Completed" + + else: + frappe.log_error(str(resp), 'Stripe Payment not completed') + + except: + frappe.log_error(frappe.get_traceback()) + # failed + pass + + status = frappe.flags.integration_request.status_code + + if self.flags.status_changed_to == "Completed": + if self.data.reference_doctype and self.data.reference_docname: + custom_redirect_to = None + try: + custom_redirect_to = frappe.get_doc(self.data.reference_doctype, + self.data.reference_docname).run_method("on_payment_authorized", self.flags.status_changed_to) + except Exception: + frappe.log_error(frappe.get_traceback()) + + if custom_redirect_to: + redirect_to = custom_redirect_to + + redirect_url = 'payment-success' + else: + redirect_url = 'payment-failed' + + if redirect_to: + redirect_url += '?' + urllib.urlencode({'redirect_to': redirect_to}) + if redirect_message: + redirect_url += '&' + urllib.urlencode({'redirect_message': redirect_message}) + + return { + "redirect_to": redirect_url, + "status": status + } diff --git a/payments/templates/includes/stripe_checkout.js b/payments/templates/includes/stripe_checkout.js new file mode 100644 index 0000000..b662012 --- /dev/null +++ b/payments/templates/includes/stripe_checkout.js @@ -0,0 +1,47 @@ +$(document).ready(function(){ + (function(e){ + var handler = StripeCheckout.configure({ + key: "{{ publishable_key }}", + token: function(token) { + // You can access the token ID with `token.id`. + // Get the token ID to your server-side code for use. + stripe.make_payment_log(token, {{ frappe.form_dict|json }}, "{{ reference_doctype }}", "{{ reference_docname }}"); + } + }); + + handler.open({ + name: "{{payer_name}}", + description: "{{description}}", + amount: cint("{{ amount }}" * 100), // 2000 paise = INR 20 + email: "{{payer_email}}", + currency: "{{currency}}" + }); + + })(); +}) + +frappe.provide('stripe'); + +stripe.make_payment_log = function(token, data, doctype, docname){ + $('.stripe-loading').addClass('hidden'); + $('.stripe-confirming').removeClass('hidden'); + frappe.call({ + method:"frappe.templates.pages.integrations.stripe_checkout.make_payment", + freeze:true, + headers: {"X-Requested-With": "XMLHttpRequest"}, + args: { + "stripe_token_id": token.id, + "data": JSON.stringify(data), + "reference_doctype": doctype, + "reference_docname": docname + }, + callback: function(r){ + if (r.message && r.message.status == 200) { + window.location.href = r.message.redirect_to + } + else if (r.message && ([401,400,500].indexOf(r.message.status) > -1)) { + window.location.href = r.message.redirect_to + } + } + }) +} diff --git a/payments/templates/pages/stripe_checkout.html b/payments/templates/pages/stripe_checkout.html new file mode 100644 index 0000000..0fe5dc0 --- /dev/null +++ b/payments/templates/pages/stripe_checkout.html @@ -0,0 +1,28 @@ +{% extends "templates/web.html" %} + +{% block title %} Payment {% endblock %} + +{%- block header -%}{% endblock %} + +{% block script %} + + +{% endblock %} + +{%- block page_content -%} + +
+ Loading Payment System + +
+ +{% endblock %} + +{% block style %} + +{% endblock %} \ No newline at end of file diff --git a/payments/templates/pages/stripe_checkout.py b/payments/templates/pages/stripe_checkout.py new file mode 100644 index 0000000..e79a10f --- /dev/null +++ b/payments/templates/pages/stripe_checkout.py @@ -0,0 +1,49 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import flt, cint +import json + +no_cache = 1 +no_sitemap = 1 + +expected_keys = ('amount', 'title', 'description', 'reference_doctype', 'reference_docname', + 'payer_name', 'payer_email', 'order_id', 'currency') + +def get_context(context): + context.no_cache = 1 + context.publishable_key = get_api_key() + + # all these keys exist in form_dict + if not (set(expected_keys) - set(frappe.form_dict.keys())): + for key in expected_keys: + context[key] = frappe.form_dict[key] + + context['amount'] = flt(context['amount']) + + else: + frappe.redirect_to_message(_('Some information is missing'), + _('Looks like someone sent you to an incomplete URL. Please ask them to look into it.')) + frappe.local.flags.redirect_location = frappe.local.response.location + raise frappe.Redirect + +def get_api_key(): + publishable_key = frappe.db.get_value("Stripe Settings", None, "publishable_key") + if cint(frappe.form_dict.get("use_sandbox")): + publishable_key = frappe.conf.sandbox_publishable_key + + return publishable_key + +@frappe.whitelist(allow_guest=True) +def make_payment(stripe_token_id, data, reference_doctype=None, reference_docname=None): + data = json.loads(data) + + data.update({ + "stripe_token_id": stripe_token_id + }) + + data = frappe.get_doc("Stripe Settings").create_request(data) + frappe.db.commit() + return data \ No newline at end of file