Browse Source

Subscription integration

develop
Charles-Henri Decultot 6 years ago
parent
commit
0134e13631
  1. 15
      erpnext/accounts/doctype/payment_request/payment_request.js
  2. 51
      erpnext/accounts/doctype/payment_request/payment_request.json
  3. 35
      erpnext/accounts/doctype/payment_request/payment_request.py
  4. 19
      erpnext/accounts/doctype/subscription/subscription.py
  5. 130
      erpnext/accounts/doctype/subscription_plan/subscription_plan.json
  6. 13
      erpnext/accounts/doctype/subscription_plan/subscription_plan.py
  7. 0
      erpnext/erpnext_integrations/doctype/payment_plan/__init__.py
  8. 6
      erpnext/erpnext_integrations/doctype/payment_plan/payment_plan.js
  9. 195
      erpnext/erpnext_integrations/doctype/payment_plan/payment_plan.json
  10. 23
      erpnext/erpnext_integrations/doctype/payment_plan/test_payment_plan.js
  11. 9
      erpnext/erpnext_integrations/doctype/payment_plan/test_payment_plan.py
  12. 0
      erpnext/erpnext_integrations/doctype/stripe_settings/__init__.py
  13. 6
      erpnext/erpnext_integrations/doctype/stripe_settings/stripe_settings.js
  14. 315
      erpnext/erpnext_integrations/doctype/stripe_settings/stripe_settings.json
  15. 159
      erpnext/erpnext_integrations/doctype/stripe_settings/stripe_settings.py
  16. 23
      erpnext/erpnext_integrations/doctype/stripe_settings/test_stripe_settings.js
  17. 9
      erpnext/erpnext_integrations/doctype/stripe_settings/test_stripe_settings.py
  18. 20
      erpnext/erpnext_integrations/stripe_integration.py
  19. 85
      erpnext/templates/includes/integrations/stripe_checkout.js
  20. 0
      erpnext/templates/pages/integrations/__init__.py
  21. 16
      erpnext/templates/pages/integrations/gocardless_checkout.html
  22. 76
      erpnext/templates/pages/integrations/gocardless_checkout.py
  23. 16
      erpnext/templates/pages/integrations/gocardless_confirmation.html
  24. 85
      erpnext/templates/pages/integrations/gocardless_confirmation.py
  25. 113
      erpnext/templates/pages/integrations/stripe_checkout.css
  26. 56
      erpnext/templates/pages/integrations/stripe_checkout.html
  27. 70
      erpnext/templates/pages/integrations/stripe_checkout.py
  28. 1
      requirements.txt

15
erpnext/accounts/doctype/payment_request/payment_request.js

@ -59,5 +59,18 @@ frappe.ui.form.on("Payment Request", "refresh", function(frm) {
frappe.ui.form.on("Payment Request", "is_a_subscription", function(frm) {
frm.toggle_reqd("payment_gateway_account", frm.doc.is_a_subscription);
frm.toggle_reqd("payment_plan", frm.doc.is_a_subscription);
frm.toggle_reqd("subscription_plans", frm.doc.is_a_subscription);
if (frm.doc.is_a_subscription) {
frappe.call({
method: "get_subscription_details",
doc: frm.doc,
freeze: true,
callback: function(r){
if(!r.exc) {
frm.refresh_field("subscription_plans");
}
}
});
}
});

51
erpnext/accounts/doctype/payment_request/payment_request.json

@ -307,6 +307,38 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_a_subscription",
"fieldtype": "Check",
"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": "Is a Subscription",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -380,9 +412,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
"fieldname": "is_a_subscription",
"fieldtype": "Check",
"depends_on": "eval:doc.is_a_subscription",
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -390,7 +424,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is a subscription",
"label": "Subscription Section",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -413,9 +447,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_a_subscription",
"fieldname": "payment_plan",
"fieldtype": "Link",
"fieldname": "subscription_plans",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -423,10 +456,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payment Plan",
"label": "Subscription Plans",
"length": 0,
"no_copy": 0,
"options": "Payment Plan",
"options": "Subscription Plan Detail",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -842,7 +875,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-05-23 11:36:49.975929",
"modified": "2018-06-20 17:06:43.850174",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

35
erpnext/accounts/doctype/payment_request/payment_request.py

@ -12,8 +12,9 @@ from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults
from frappe.integrations.utils import get_payment_gateway_controller
from frappe.utils.background_jobs import enqueue
from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription
class PaymentRequest(Document):
class PaymentRequest(Document):
def validate(self):
self.validate_reference_document()
self.validate_payment_request()
@ -33,6 +34,25 @@ class PaymentRequest(Document):
if self.payment_account and ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"):
frappe.throw(_("Transaction currency must be same as Payment Gateway currency"))
def on_update(self):
self.validate_subscription_details()
def validate_subscription_details(self):
if self.is_a_subscription:
amount = 0
for subscription_plan in self.subscription_plans:
plan = frappe.get_doc("Subscription Plan", subscription_plan.plan)
if plan.payment_gateway != self.payment_gateway_account:
frappe.throw(_('The payment gateway account in plan {0} is different from the payment gateway account in this payment request'.format(plan.name)))
rate = plan.get_plan_rate()
frappe.log_error(rate)
amount += rate
if amount != self.grand_total:
frappe.msgprint(_("The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.".format(self.grand_total, amount)))
def on_submit(self):
send_mail = self.payment_gateway_validation()
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
@ -235,6 +255,19 @@ class PaymentRequest(Document):
return redirect_to
def create_subscription(self, payment_provider, gateway_controller, data):
if payment_provider == "stripe":
return create_stripe_subscription(gateway_controller, data)
def get_subscription_details(self):
if self.reference_doctype == "Sales Invoice":
subscriptions = frappe.db.sql("""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice='{0}'""".format(self.reference_name), as_dict=1)
self.subscription_plans = []
for subscription in subscriptions:
plans = frappe.get_doc("Subscription", subscription.sub_name).plans
for plan in plans:
self.append('subscription_plans', plan)
@frappe.whitelist(allow_guest=True)
def make_payment_request(**args):
"""Make payment request"""

19
erpnext/accounts/doctype/subscription/subscription.py

@ -8,7 +8,6 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils.data import nowdate, getdate, cint, add_days, date_diff, get_last_day, add_to_date, flt
from erpnext.utilities.product import get_price
class Subscription(Document):
@ -300,21 +299,13 @@ class Subscription(Document):
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
items = []
customer = self.get_customer(self.subscriber)
for plan in plans:
subscription_plan = frappe.get_doc("Subscription Plan", plan.plan)
if subscription_plan.price_determination == "Fixed rate":
if not prorate:
items.append({'item_code': subscription_plan.item, 'qty': plan.qty, 'rate': subscription_plan.cost})
else:
items.append({'item_code': subscription_plan.item, 'qty': plan.qty, 'rate': (subscription_plan.cost * prorate_factor)})
elif subscription_plan.price_determination == "Based on price list":
customer = self.get_customer(self.subscriber)
customer_group = frappe.db.get_value("Customer", customer, "customer_group")
rate = get_price(item_code=subscription_plan.item, price_list=subscription_plan.price_list, customer_group=customer_group, company=None, qty=plan.qty)
if not prorate:
items.append({'item_code': subscription_plan.item, 'qty': plan.qty, 'rate': rate})
else:
items.append({'item_code': subscription_plan.item, 'qty': plan.qty, 'rate': (rate * prorate_factor)})
if not prorate:
items.append({'item_code': subscription_plan.item, 'qty': plan.qty, 'rate': subscription_plan.get_plan_rate(customer)})
else:
items.append({'item_code': subscription_plan.item, 'qty': plan.qty, 'rate': (subscription_plan.get_plan_rate(customer) * prorate_factor)})
return items

130
erpnext/accounts/doctype/subscription_plan/subscription_plan.json

@ -434,6 +434,134 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_plan_section",
"fieldtype": "Section Break",
"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": "Payment Plan",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_plan_id",
"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": "Payment Plan",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_16",
"fieldtype": "Column Break",
"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,
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_gateway",
"fieldtype": "Link",
"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": "Payment Gateway",
"length": 0,
"no_copy": 0,
"options": "Payment Gateway Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -446,7 +574,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-06-20 15:43:10.152762",
"modified": "2018-06-20 16:59:54.082358",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Plan",

13
erpnext/accounts/doctype/subscription_plan/subscription_plan.py

@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from erpnext.utilities.product import get_price
class SubscriptionPlan(Document):
def validate(self):
@ -13,3 +14,15 @@ class SubscriptionPlan(Document):
def validate_interval_count(self):
if self.billing_interval_count < 1:
frappe.throw('Billing Interval Count cannot be less than 1')
def get_plan_rate(self, quantity=1, customer=None):
if self.price_determination == "Fixed rate":
return self.cost
elif self.price_determination == "Based on price list":
if customer:
customer_group = frappe.db.get_value("Customer", customer, "customer_group")
else:
customer_group = None
return get_price(item_code=self.item, price_list=self.price_list, customer_group=customer_group, company=None, qty=quantity).price_list_rate

0
erpnext/erpnext_integrations/doctype/payment_plan/__init__.py

6
erpnext/erpnext_integrations/doctype/payment_plan/payment_plan.js

@ -1,6 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Payment Plan', {
});

195
erpnext/erpnext_integrations/doctype/payment_plan/payment_plan.json

@ -1,195 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:payment_plan_id",
"beta": 0,
"creation": "2018-05-23 10:17:31.108746",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_plan",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Name",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_plan_id",
"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": "ID",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_gateway",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Payment Gateway",
"length": 0,
"no_copy": 0,
"options": "Payment Gateway Account",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Monthly",
"description": "",
"fieldname": "recurrence",
"fieldtype": "Select",
"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": "Recurrence",
"length": 0,
"no_copy": 0,
"options": "Daily\nWeekly\nMonthly\nEvery 3 Months\nEvery 6 Months\nYearly",
"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,
"translatable": 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": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-05-23 18:25:48.200621",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Payment Plan",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"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",
"title_field": "payment_plan",
"track_changes": 1,
"track_seen": 0
}

23
erpnext/erpnext_integrations/doctype/payment_plan/test_payment_plan.js

@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Payment Plan", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Payment Plan
() => frappe.tests.make('Payment Plan', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

9
erpnext/erpnext_integrations/doctype/payment_plan/test_payment_plan.py

@ -1,9 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestPaymentPlan(unittest.TestCase):
pass

0
erpnext/erpnext_integrations/doctype/stripe_settings/__init__.py

6
erpnext/erpnext_integrations/doctype/stripe_settings/stripe_settings.js

@ -1,6 +0,0 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Stripe Settings', {
});

315
erpnext/erpnext_integrations/doctype/stripe_settings/stripe_settings.json

@ -1,315 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:gateway_name",
"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_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "gateway_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Payment Gateway Name",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"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": 1,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"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,
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 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": 1,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"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,
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "header_img",
"fieldtype": "Attach Image",
"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": "Header Image",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_7",
"fieldtype": "Column Break",
"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,
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "redirect_url",
"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": "Redirect URL",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 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": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-05-23 18:15:55.584782",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Stripe Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 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
}

159
erpnext/erpnext_integrations/doctype/stripe_settings/stripe_settings.py

@ -1,159 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, 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 _
from six.moves.urllib.parse import urlencode
from frappe.utils import get_url, call_hook_method, cint, flt
from frappe.integrations.utils import make_get_request, create_request_log, create_payment_gateway
import stripe
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"
]
currency_wise_minimum_charge_amount = {
'JPY': 50, 'MXN': 10, 'DKK': 2.50, 'HKD': 4.00, 'NOK': 3.00, 'SEK': 3.00,
'USD': 0.50, 'AUD': 0.50, 'BRL': 0.50, 'CAD': 0.50, 'CHF': 0.50, 'EUR': 0.50,
'GBP': 0.30, 'NZD': 0.50, 'SGD': 0.50
}
def on_update(self):
create_payment_gateway('Stripe-' + self.gateway_name, settings='Stripe Settings', controller=self.gateway_name)
call_hook_method('payment_gateway_enabled', gateway='Stripe-' + self.gateway_name)
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 validate_minimum_transaction_amount(self, currency, amount):
if currency in self.currency_wise_minimum_charge_amount:
if flt(amount) < self.currency_wise_minimum_charge_amount.get(currency, 0.0):
frappe.throw(_("For currency {0}, the minimum transaction amount should be {1}").format(currency,
self.currency_wise_minimum_charge_amount.get(currency, 0.0)))
def get_payment_url(self, **kwargs):
return get_url("./integrations/stripe_checkout?{0}".format(urlencode(kwargs)))
def create_request(self, data):
self.data = frappe._dict(data)
stripe.api_key = self.get_password(fieldname="secret_key", raise_exception=False)
stripe.default_http_client = stripe.http_client.RequestsClient()
try:
self.integration_request = create_request_log(self.data, "Host", "Stripe")
if frappe.db.get_value("Payment Request", self.data.reference_docname, 'is_a_subscription'):
self.payment_plan = frappe.db.get_value("Payment Request", self.data.reference_docname, 'payment_plan')
return self.create_subscription_on_stripe()
else:
return self.create_charge_on_stripe()
except Exception:
frappe.log_error(frappe.get_traceback())
return{
"redirect_to": frappe.redirect_to_message(_('Server Error'), _("It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.")),
"status": 401
}
def create_charge_on_stripe(self):
try:
charge = stripe.Charge.create(amount=cint(flt(self.data.amount)*100), currency=self.data.currency, source=self.data.stripe_token_id, description=self.data.description)
if charge.captured == True:
self.integration_request.db_set('status', 'Completed', update_modified=False)
self.flags.status_changed_to = "Completed"
else:
frappe.log_error(charge.failure_message, 'Stripe Payment not completed')
except Exception:
frappe.log_error(frappe.get_traceback())
return self.finalize_request()
def create_subscription_on_stripe(self):
items = [
{
"plan": self.payment_plan
}
]
try:
customer = stripe.Customer.create(description=self.data.payer_name, email=self.data.payer_email, source=self.data.stripe_token_id)
subscription = stripe.Subscription.create(customer=customer, items=items)
if subscription.status == "active":
self.integration_request.db_set('status', 'Completed', update_modified=False)
self.flags.status_changed_to = "Completed"
else:
self.integration_request.db_set('status', 'Failed', update_modified=False)
frappe.log_error('Subscription N°: ' + subscription.id, 'Stripe Payment not completed')
except Exception:
self.integration_request.db_set('status', 'Failed', update_modified=False)
frappe.log_error(frappe.get_traceback())
return self.finalize_request()
def finalize_request(self):
redirect_to = self.data.get('redirect_to') or None
redirect_message = self.data.get('redirect_message') or None
status = self.integration_request.status
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'
if self.redirect_url:
redirect_url = self.redirect_url
redirect_to = None
else:
redirect_url = 'payment-failed'
if redirect_to:
redirect_url += '?' + urlencode({'redirect_to': redirect_to})
if redirect_message:
redirect_url += '&' + urlencode({'redirect_message': redirect_message})
return {
"redirect_to": redirect_url,
"status": status
}
def get_gateway_controller(doc):
payment_request = frappe.get_doc("Payment Request", doc)
gateway_controller = frappe.db.get_value("Payment Gateway", payment_request.payment_gateway, "gateway_controller")
return gateway_controller

23
erpnext/erpnext_integrations/doctype/stripe_settings/test_stripe_settings.js

@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Stripe Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Stripe Settings
() => frappe.tests.make('Stripe Settings', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

9
erpnext/erpnext_integrations/doctype/stripe_settings/test_stripe_settings.py

@ -1,9 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestStripeSettings(unittest.TestCase):
pass

20
erpnext/erpnext_integrations/doctype/payment_plan/payment_plan.py → erpnext/erpnext_integrations/stripe_integration.py

@ -9,10 +9,6 @@ from frappe import _
from frappe.integrations.utils import create_request_log
import stripe
class PaymentPlan(Document):
pass
def create_stripe_subscription(gateway_controller, data):
stripe_settings = frappe.get_doc("Stripe Settings", gateway_controller)
stripe_settings.data = frappe._dict(data)
@ -22,7 +18,7 @@ def create_stripe_subscription(gateway_controller, data):
try:
stripe_settings.integration_request = create_request_log(stripe_settings.data, "Host", "Stripe")
stripe_settings.payment_plan = frappe.db.get_value("Payment Request", stripe_settings.data.reference_docname, 'payment_plan')
stripe_settings.payment_plans = frappe.get_doc("Payment Request", stripe_settings.data.reference_docname).subscription_plans
return create_subscription_on_stripe(stripe_settings)
except Exception:
@ -34,11 +30,13 @@ def create_stripe_subscription(gateway_controller, data):
def create_subscription_on_stripe(stripe_settings):
items = [
{
"plan": stripe_settings.payment_plan
}
]
items = []
for payment_plan in stripe_settings.payment_plans:
plan = frappe.db.get_value("Subscription Plan", payment_plan.plan, "payment_plan_id")
items.append({"plan": plan, "quantity": payment_plan.qty})
frappe.log_error(items, 'Items')
try:
customer = stripe.Customer.create(description=stripe_settings.data.payer_name, email=stripe_settings.data.payer_email, source=stripe_settings.data.stripe_token_id)
@ -56,4 +54,4 @@ def create_subscription_on_stripe(stripe_settings):
stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False)
frappe.log_error(frappe.get_traceback())
return stripe_settings.finalize_request()
return stripe_settings.finalize_request()

85
erpnext/templates/includes/integrations/stripe_checkout.js

@ -1,85 +0,0 @@
var stripe = Stripe("{{ publishable_key }}");
var elements = stripe.elements();
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
var card = elements.create('card', {
hidePostalCode: true,
style: style
});
card.mount('#card-element');
function setOutcome(result) {
if (result.token) {
$('#submit').prop('disabled', true)
$('#submit').html(__('Processing...'))
frappe.call({
method:"erpnext.templates.pages.integrations.stripe_checkout.make_payment",
freeze:true,
headers: {"X-Requested-With": "XMLHttpRequest"},
args: {
"stripe_token_id": result.token.id,
"data": JSON.stringify({{ frappe.form_dict|json }}),
"reference_doctype": "{{ reference_doctype }}",
"reference_docname": "{{ reference_docname }}"
},
callback: function(r) {
if (r.message.status == "Completed") {
$('#submit').hide()
$('.success').show()
setTimeout(function() {
window.location.href = r.message.redirect_to
}, 2000);
} else {
$('#submit').hide()
$('.error').show()
setTimeout(function() {
window.location.href = r.message.redirect_to
}, 2000);
}
}
});
} else if (result.error) {
$('.error').html() = result.error.message;
$('.error').show()
}
}
card.on('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
frappe.ready(function() {
$('#submit').off("click").on("click", function(e) {
e.preventDefault();
var extraDetails = {
name: $('input[name=cardholder-name]').val(),
email: $('input[name=cardholder-email]').val()
}
stripe.createToken(card, extraDetails).then(setOutcome);
})
});

0
erpnext/templates/pages/integrations/__init__.py

16
erpnext/templates/pages/integrations/gocardless_checkout.html

@ -1,16 +0,0 @@
{% extends "templates/web.html" %}
{% block title %} Payment {% endblock %}
{%- block header -%}{% endblock %}
{% block script %}
<script>{% include "templates/includes/integrations/gocardless_checkout.js" %}</script>
{% endblock %}
{%- block page_content -%}
<p class='lead text-center centered'>
<span class='gocardless-loading'>{{ _("Loading Payment System") }}</span>
</p>
{% endblock %}

76
erpnext/templates/pages/integrations/gocardless_checkout.py

@ -1,76 +0,0 @@
# Copyright (c) 2018, 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
import json
from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import gocardless_initialization, get_gateway_controller
from frappe.utils import get_url
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
# 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'])
gateway_controller = get_gateway_controller(context.reference_docname)
context['header_img'] = frappe.db.get_value("GoCardless Settings", gateway_controller, "header_img")
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
@frappe.whitelist(allow_guest=True)
def check_mandate(data, reference_doctype, reference_docname):
data = json.loads(data)
client = gocardless_initialization(reference_docname)
payer = frappe.get_doc("Customer", data["payer_name"])
if payer.customer_type == "Individual" and payer.customer_primary_contact is not None:
primary_contact = frappe.get_doc("Contact", payer.customer_primary_contact)
prefilled_customer = {
"company_name": payer.name,
"given_name": primary_contact.first_name,
"family_name": primary_contact.last_name,
}
if primary_contact.email_id is not None:
prefilled_customer.update({"email": primary_contact.email_id})
else:
prefilled_customer.update({"email": frappe.session.user})
else:
prefilled_customer = {
"company_name": payer.name,
"email": frappe.session.user
}
success_url = get_url("./integrations/gocardless_confirmation?reference_doctype=" + reference_doctype + "&reference_docname=" + reference_docname)
try:
redirect_flow = client.redirect_flows.create(params={
"description": _("Pay {0} {1}".format(data['amount'], data['currency'])),
"session_token": frappe.session.user,
"success_redirect_url": success_url,
"prefilled_customer": prefilled_customer
})
return {"redirect_to": redirect_flow.redirect_url}
except Exception as e:
frappe.log_error(e, "GoCardless Payment Error")
return {"redirect_to": '/integrations/payment-failed'}

16
erpnext/templates/pages/integrations/gocardless_confirmation.html

@ -1,16 +0,0 @@
{% extends "templates/web.html" %}
{% block title %} Payment {% endblock %}
{%- block header -%}{% endblock %}
{% block script %}
<script>{% include "templates/includes/integrations/gocardless_confirmation.js" %}</script>
{% endblock %}
{%- block page_content -%}
<p class='lead text-center centered'>
<span class='gocardless-loading'>{{ _("Payment Confirmation") }}</span>
</p>
{% endblock %}

85
erpnext/templates/pages/integrations/gocardless_confirmation.py

@ -1,85 +0,0 @@
# Copyright (c) 2018, 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 erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import gocardless_initialization, get_gateway_controller
no_cache = 1
no_sitemap = 1
expected_keys = ('redirect_flow_id', 'reference_doctype', 'reference_docname')
def get_context(context):
context.no_cache = 1
# 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]
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
@frappe.whitelist(allow_guest=True)
def confirm_payment(redirect_flow_id, reference_doctype, reference_docname):
client = gocardless_initialization(reference_docname)
try:
redirect_flow = client.redirect_flows.complete(
redirect_flow_id,
params={
"session_token": frappe.session.user
})
data = {
"mandate": redirect_flow.links.mandate,
"customer": redirect_flow.links.customer,
"redirect_to": redirect_flow.confirmation_url,
"redirect_message": "Mandate successfully created",
"reference_doctype": reference_doctype,
"reference_docname": reference_docname
}
try:
create_mandate(data)
except Exception as e:
frappe.log_error(e, "GoCardless Mandate Registration Error")
gateway_controller = get_gateway_controller(reference_docname)
frappe.get_doc("GoCardless Settings", gateway_controller).create_payment_request(data)
return {"redirect_to": redirect_flow.confirmation_url}
except Exception as e:
frappe.log_error(e, "GoCardless Payment Error")
return {"redirect_to": '/integrations/payment-failed'}
def create_mandate(data):
data = frappe._dict(data)
frappe.logger().debug(data)
mandate = data.get('mandate')
if frappe.db.exists("GoCardless Mandate", mandate):
return
else:
reference_doc = frappe.db.get_value(data.get('reference_doctype'), data.get('reference_docname'), ["reference_doctype", "reference_name"], as_dict=1)
erpnext_customer = frappe.db.get_value(reference_doc.reference_doctype, reference_doc.reference_name, ["customer_name"], as_dict=1)
try:
frappe.get_doc({
"doctype": "GoCardless Mandate",
"mandate": mandate,
"customer": erpnext_customer.customer_name,
"gocardless_customer": data.get('customer')
}).insert(ignore_permissions=True)
except Exception:
frappe.log_error(frappe.get_traceback())

113
erpnext/templates/pages/integrations/stripe_checkout.css

@ -1,113 +0,0 @@
.StripeElement {
background-color: white;
height: 40px;
padding: 10px 12px;
border-radius: 4px;
border: 1px solid transparent;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.StripeElement--webkit-autofill {
background-color: #fefde5;
}
.stripe #payment-form {
margin-top: 80px;
}
.stripe button {
float: right;
display: block;
background: #5e64ff;
color: white;
box-shadow: 0 7px 14px 0 rgba(49, 49, 93, 0.10), 0 3px 6px 0 rgba(0, 0, 0, 0.08);
border-radius: 4px;
border: 0;
margin-top: 20px;
font-size: 15px;
font-weight: 400;
max-width: 40%;
height: 40px;
line-height: 38px;
outline: none;
}
.stripe button:hover, .stripe button:focus {
background: #2b33ff;
border-color: #0711ff;
}
.stripe button:active {
background: #5e64ff;
}
.stripe button:disabled {
background: #515e80;
}
.stripe .group {
background: white;
box-shadow: 2px 7px 14px 2px rgba(49, 49, 93, 0.10), 0 3px 6px 0 rgba(0, 0, 0, 0.08);
border-radius: 4px;
margin-bottom: 20px;
}
.stripe label {
position: relative;
color: #8898AA;
font-weight: 300;
height: 40px;
line-height: 40px;
margin-left: 20px;
display: block;
}
.stripe .group label:not(:last-child) {
border-bottom: 1px solid #F0F5FA;
}
.stripe label>span {
width: 20%;
text-align: right;
float: left;
}
.current-card {
margin-left: 20px;
}
.field {
background: transparent;
font-weight: 300;
border: 0;
color: #31325F;
outline: none;
padding-right: 10px;
padding-left: 10px;
cursor: text;
width: 70%;
height: 40px;
float: right;
}
.field::-webkit-input-placeholder {
color: #CFD7E0;
}
.field::-moz-placeholder {
color: #CFD7E0;
}
.field:-ms-input-placeholder {
color: #CFD7E0;
}

56
erpnext/templates/pages/integrations/stripe_checkout.html

@ -1,56 +0,0 @@
{% extends "templates/web.html" %}
{% block title %} Payment {% endblock %}
{%- block header -%}
{% endblock %}
{% block script %}
<script src="https://js.stripe.com/v3/"></script>
<script>{% include "templates/includes/integrations/stripe_checkout.js" %}</script>
{% endblock %}
{%- block page_content -%}
<div class="row stripe" style="min-height: 400px; padding-bottom: 50px; margin-top:100px;">
<div class="col-sm-8 col-sm-offset-2">
<img src={{image}}>
<h2 class="text-center">{{description}}</h2>
<form id="payment-form">
<div class="form-row">
<div class="group">
<div>
<label>
<span>{{ _("Name") }}</span>
<input id="cardholder-name" name="cardholder-name" class="field" placeholder="{{ _('John Doe') }}" value="{{payer_name}}"/>
</label>
</div>
</div>
<div class="group">
<div>
<label>
<span>{{ _("Email") }}</span>
<input id="cardholder-email" name="cardholder-email" class="field" placeholder="{{ _('john@doe.com') }}" value="{{payer_email}}"/>
</label>
</div>
</div>
<div class="group">
<label>
<span>{{ _("Card Details") }}</span>
<div id="card-element" name="card-element" class="field"></div>
<div id="card-errors" role="alert"></div>
</label>
</div>
</div>
<button type="submit" class="submit" id="submit">{{_('Pay')}} {{amount}}</button>
<div class="outcome text-center">
<div class="error" hidden>{{ _("An error occured during the payment process. Please contact us.") }}</div>
<div class="success" hidden>{{ _("Your payment has been successfully registered.") }}</div>
</div>
</form>
</div>
</div>
{% endblock %}

70
erpnext/templates/pages/integrations/stripe_checkout.py

@ -1,70 +0,0 @@
# 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 cint, fmt_money
import json
from erpnext.erpnext_integrations.doctype.stripe_settings.stripe_settings import get_gateway_controller
from erpnext.erpnext_integrations.doctype.payment_plan.payment_plan import create_stripe_subscription
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
# all these keys exist in form_dict
if not (set(expected_keys) - set(list(frappe.form_dict))):
for key in expected_keys:
context[key] = frappe.form_dict[key]
gateway_controller = get_gateway_controller(context.reference_docname)
context.publishable_key = get_api_key(context.reference_docname, gateway_controller)
context.image = get_header_image(context.reference_docname, gateway_controller)
context['amount'] = fmt_money(amount=context['amount'], currency=context['currency'])
if frappe.db.get_value(context.reference_doctype, context.reference_docname, "is_a_subscription"):
payment_plan = frappe.db.get_value(context.reference_doctype, context.reference_docname, "payment_plan")
recurrence = frappe.db.get_value("Payment Plan", payment_plan, "recurrence")
context['amount'] = context['amount'] + " " + _(recurrence)
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(doc, gateway_controller):
publishable_key = frappe.db.get_value("Stripe Settings", gateway_controller, "publishable_key")
if cint(frappe.form_dict.get("use_sandbox")):
publishable_key = frappe.conf.sandbox_publishable_key
return publishable_key
def get_header_image(doc, gateway_controller):
header_image = frappe.db.get_value("Stripe Settings", gateway_controller, "header_img")
return header_image
@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
})
gateway_controller = get_gateway_controller(reference_docname)
if frappe.db.get_value("Payment Request", reference_docname, 'is_a_subscription'):
data = create_stripe_subscription(gateway_controller, data)
else:
data = frappe.get_doc("Stripe Settings", gateway_controller).create_request(data)
frappe.db.commit()
return data

1
requirements.txt

@ -6,4 +6,3 @@ python-stdnum
braintree
gocardless_pro
woocommerce
stripe

Loading…
Cancel
Save