Charles-Henri Decultot
6 years ago
28 changed files with 246 additions and 1300 deletions
@ -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', { |
|||
|
|||
}); |
@ -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 |
|||
} |
@ -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() |
|||
]); |
|||
|
|||
}); |
@ -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 |
@ -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', { |
|||
|
|||
}); |
@ -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 |
|||
} |
@ -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 |
@ -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() |
|||
]); |
|||
|
|||
}); |
@ -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 |
@ -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); |
|||
}) |
|||
}); |
@ -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 %} |
@ -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'} |
@ -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 %} |
@ -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()) |
@ -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; |
|||
} |
@ -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 %} |
@ -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 |
Loading…
Reference in new issue