mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
onboarding init
This commit is contained in:
parent
772c1f52d7
commit
98d201968e
35 changed files with 7521 additions and 268 deletions
|
@ -18,16 +18,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "path",
|
"type": "path",
|
||||||
"url": "modules.local/ppcp-button"
|
"url": "modules.local/ppcp-onboarding"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "path",
|
|
||||||
"url": "modules.local/ppcp-wc-gateway"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"dhii/module-interface": "0.2.x-dev",
|
"dhii/module-interface": "0.2.x-dev",
|
||||||
"psr/container": "^1.0",
|
"psr/container": "^1.0",
|
||||||
|
"inpsyde/ppcp-onboarding": "dev-master",
|
||||||
"inpsyde/ppcp-button": "dev-master",
|
"inpsyde/ppcp-button": "dev-master",
|
||||||
"inpsyde/ppcp-wc-gateway": "dev-master",
|
"inpsyde/ppcp-wc-gateway": "dev-master",
|
||||||
"oomphinc/composer-installers-extender": "^1.1",
|
"oomphinc/composer-installers-extender": "^1.1",
|
||||||
|
@ -42,7 +39,9 @@
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Inpsyde\\PayPalCommerce\\": "src/"
|
"Inpsyde\\PayPalCommerce\\": "src/",
|
||||||
|
"Inpsyde\\PayPalCommerce\\Button\\": "modules.local/ppcp-button/src/",
|
||||||
|
"Inpsyde\\PayPalCommerce\\WcGateway\\": "modules.local/ppcp-wc-gateway/src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
|
|
|
@ -13,12 +13,20 @@ use Inpsyde\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint;
|
||||||
use Inpsyde\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
|
use Inpsyde\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint;
|
||||||
use Inpsyde\PayPalCommerce\Button\Endpoint\RequestData;
|
use Inpsyde\PayPalCommerce\Button\Endpoint\RequestData;
|
||||||
use Inpsyde\PayPalCommerce\Button\Exception\RuntimeException;
|
use Inpsyde\PayPalCommerce\Button\Exception\RuntimeException;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\State;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'button.smart-button' => static function (ContainerInterface $container): SmartButtonInterface {
|
'button.smart-button' => static function (ContainerInterface $container): SmartButtonInterface {
|
||||||
|
|
||||||
|
$state = $container->get('onboarding.state');
|
||||||
|
/**
|
||||||
|
* @var State $state
|
||||||
|
*/
|
||||||
|
if ($state->currentState() < State::STATE_PROGRESSIVE) {
|
||||||
|
return new DisabledSmartButton();
|
||||||
|
}
|
||||||
$settings = $container->get('wcgateway.settings');
|
$settings = $container->get('wcgateway.settings');
|
||||||
if (! wc_string_to_bool($settings->get('enabled'))) {
|
if (!$settings->has('enabled') || ! wc_string_to_bool($settings->get('enabled'))) {
|
||||||
return new DisabledSmartButton();
|
return new DisabledSmartButton();
|
||||||
}
|
}
|
||||||
$payeeRepository = $container->get('api.repository.payee');
|
$payeeRepository = $container->get('api.repository.payee');
|
||||||
|
|
|
@ -68,35 +68,54 @@ class SmartButton implements SmartButtonInterface
|
||||||
esc_html__('Pay with Card', 'woocommerce-paypal-commerce-gateway')
|
esc_html__('Pay with Card', 'woocommerce-paypal-commerce-gateway')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if (is_cart() && wc_string_to_bool($this->settings->get('button_cart_enabled'))) {
|
if (
|
||||||
|
is_cart()
|
||||||
|
&& $this->settings->has('button_cart_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('button_cart_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_proceed_to_checkout',
|
'woocommerce_proceed_to_checkout',
|
||||||
$buttonRenderer,
|
$buttonRenderer,
|
||||||
20
|
20
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (is_cart() && wc_string_to_bool($this->settings->get('dcc_cart_enabled'))) {
|
if (
|
||||||
|
is_cart()
|
||||||
|
&& $this->settings->has('dcc_cart_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('dcc_cart_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_proceed_to_checkout',
|
'woocommerce_proceed_to_checkout',
|
||||||
$dccRenderer,
|
$dccRenderer,
|
||||||
20
|
20
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (is_product() && wc_string_to_bool($this->settings->get('button_single_product_enabled'))) {
|
if (
|
||||||
|
is_product()
|
||||||
|
&& $this->settings->has('button_single_product_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('button_single_product_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_single_product_summary',
|
'woocommerce_single_product_summary',
|
||||||
$buttonRenderer,
|
$buttonRenderer,
|
||||||
31
|
31
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (is_product() && wc_string_to_bool($this->settings->get('dcc_single_product_enabled'))) {
|
if (
|
||||||
|
is_product()
|
||||||
|
&& $this->settings->has('dcc_single_product_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('dcc_single_product_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_single_product_summary',
|
'woocommerce_single_product_summary',
|
||||||
$dccRenderer,
|
$dccRenderer,
|
||||||
31
|
31
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (wc_string_to_bool($this->settings->get('button_mini_cart_enabled'))) {
|
if (
|
||||||
|
$this->settings->has('button_mini_cart_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('button_mini_cart_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_widget_shopping_cart_after_buttons',
|
'woocommerce_widget_shopping_cart_after_buttons',
|
||||||
static function () {
|
static function () {
|
||||||
|
@ -105,7 +124,10 @@ class SmartButton implements SmartButtonInterface
|
||||||
30
|
30
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (wc_string_to_bool($this->settings->get('dcc_mini_cart_enabled'))) {
|
if (
|
||||||
|
$this->settings->has('dcc_mini_cart_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('dcc_mini_cart_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_widget_shopping_cart_after_buttons',
|
'woocommerce_widget_shopping_cart_after_buttons',
|
||||||
static function () use ($dccRenderer) {
|
static function () use ($dccRenderer) {
|
||||||
|
@ -119,7 +141,11 @@ class SmartButton implements SmartButtonInterface
|
||||||
$buttonRenderer,
|
$buttonRenderer,
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
if (wc_string_to_bool($this->settings->get('dcc_checkout_enabled'))) {
|
if (
|
||||||
|
|
||||||
|
$this->settings->has('dcc_checkout_enabled')
|
||||||
|
&& wc_string_to_bool($this->settings->get('dcc_checkout_enabled'))
|
||||||
|
) {
|
||||||
add_action(
|
add_action(
|
||||||
'woocommerce_review_order_after_submit',
|
'woocommerce_review_order_after_submit',
|
||||||
$dccRenderer,
|
$dccRenderer,
|
||||||
|
@ -132,7 +158,7 @@ class SmartButton implements SmartButtonInterface
|
||||||
public function enqueue(): bool
|
public function enqueue(): bool
|
||||||
{
|
{
|
||||||
wp_enqueue_script(
|
wp_enqueue_script(
|
||||||
'paypal-smart-button',
|
'ppcp-smart-button',
|
||||||
$this->moduleUrl . '/assets/js/button.js',
|
$this->moduleUrl . '/assets/js/button.js',
|
||||||
['jquery'],
|
['jquery'],
|
||||||
1,
|
1,
|
||||||
|
@ -140,7 +166,7 @@ class SmartButton implements SmartButtonInterface
|
||||||
);
|
);
|
||||||
|
|
||||||
wp_localize_script(
|
wp_localize_script(
|
||||||
'paypal-smart-button',
|
'ppcp-smart-button',
|
||||||
'PayPalCommerceGateway',
|
'PayPalCommerceGateway',
|
||||||
$this->localizeScript()
|
$this->localizeScript()
|
||||||
);
|
);
|
||||||
|
@ -149,6 +175,7 @@ class SmartButton implements SmartButtonInterface
|
||||||
|
|
||||||
private function localizeScript(): array
|
private function localizeScript(): array
|
||||||
{
|
{
|
||||||
|
the_post_thumbnail();
|
||||||
$localize = [
|
$localize = [
|
||||||
'script_attributes' => $this->attributes(),
|
'script_attributes' => $this->attributes(),
|
||||||
'redirect' => wc_get_checkout_url(),
|
'redirect' => wc_get_checkout_url(),
|
||||||
|
@ -175,8 +202,8 @@ class SmartButton implements SmartButtonInterface
|
||||||
'url' => $this->url(),
|
'url' => $this->url(),
|
||||||
'style' => [
|
'style' => [
|
||||||
'layout' => 'vertical',
|
'layout' => 'vertical',
|
||||||
'color' => $this->settings->get('button_color'),
|
'color' => ($this->settings->has('button_color')) ? $this->settings->get('button_color') : null,
|
||||||
'shape' => $this->settings->get('button_shape'),
|
'shape' => ($this->settings->has('button_shape')) ? $this->settings->get('button_shape') : null,
|
||||||
'label' => 'paypal',
|
'label' => 'paypal',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -217,7 +244,7 @@ class SmartButton implements SmartButtonInterface
|
||||||
//ToDo: Probably only needed, when DCC
|
//ToDo: Probably only needed, when DCC
|
||||||
'vault' => $this->dccIsEnabled() ? 'false' : 'false',
|
'vault' => $this->dccIsEnabled() ? 'false' : 'false',
|
||||||
'commit' => is_checkout() ? 'true' : 'false',
|
'commit' => is_checkout() ? 'true' : 'false',
|
||||||
'intent' => $this->settings->get('intent'),
|
'intent' => ($this->settings->has('intent')) ? $this->settings->get('intent') : 'commit',
|
||||||
];
|
];
|
||||||
if (defined('WP_DEBUG') && \WP_DEBUG && WC()->customer) {
|
if (defined('WP_DEBUG') && \WP_DEBUG && WC()->customer) {
|
||||||
$params['buyer-country'] = WC()->customer->get_billing_country();
|
$params['buyer-country'] = WC()->customer->get_billing_country();
|
||||||
|
@ -226,7 +253,7 @@ class SmartButton implements SmartButtonInterface
|
||||||
if ($payee->merchantId()) {
|
if ($payee->merchantId()) {
|
||||||
$params['merchant-id'] = $payee->merchantId();
|
$params['merchant-id'] = $payee->merchantId();
|
||||||
}
|
}
|
||||||
$disableFunding = $this->settings->get('disable_funding');
|
$disableFunding = $this->settings->has('disable_funding') ? $this->settings->get('disable_funding') : [];
|
||||||
if (is_array($disableFunding) && count($disableFunding)) {
|
if (is_array($disableFunding) && count($disableFunding)) {
|
||||||
$params['disable-funding'] = implode(',', $disableFunding);
|
$params['disable-funding'] = implode(',', $disableFunding);
|
||||||
}
|
}
|
||||||
|
@ -273,10 +300,17 @@ class SmartButton implements SmartButtonInterface
|
||||||
|
|
||||||
private function dccIsEnabled() : bool
|
private function dccIsEnabled() : bool
|
||||||
{
|
{
|
||||||
return
|
$keys = [
|
||||||
wc_string_to_bool($this->settings->get('dcc_cart_enabled'))
|
'dcc_cart_enabled',
|
||||||
|| wc_string_to_bool($this->settings->get('dcc_mini_cart_enabled'))
|
'dcc_mini_cart_enabled',
|
||||||
|| wc_string_to_bool($this->settings->get('dcc_checkout_enabled'))
|
'dcc_checkout_enabled',
|
||||||
|| wc_string_to_bool($this->settings->get('dcc_single_product_enabled'));
|
'dcc_single_product_enabled',
|
||||||
|
];
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if ($this->settings->has($key) && wc_string_to_bool($this->settings->get($key))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
modules.local/ppcp-onboarding/.babelrc
Normal file
5
modules.local/ppcp-onboarding/.babelrc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"babel-plugin-transform-object-rest-spread"
|
||||||
|
]
|
||||||
|
}
|
1
modules.local/ppcp-onboarding/.gitignore
vendored
Normal file
1
modules.local/ppcp-onboarding/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/node_modules/
|
2
modules.local/ppcp-onboarding/assets/js/button.js
Normal file
2
modules.local/ppcp-onboarding/assets/js/button.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t){alert("hello")}]);
|
||||||
|
//# sourceMappingURL=button.js.map
|
1
modules.local/ppcp-onboarding/assets/js/button.js.map
Normal file
1
modules.local/ppcp-onboarding/assets/js/button.js.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./resources/js/onboarding.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","alert"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,gBClFrDC,MAAM","file":"js/button.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","alert('hello');"],"sourceRoot":""}
|
50
modules.local/ppcp-onboarding/assets/js/onboarding.js
Normal file
50
modules.local/ppcp-onboarding/assets/js/onboarding.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
|
||||||
|
const onboardingCallback = (authCode, sharedId) => {
|
||||||
|
console.log(authCode);
|
||||||
|
console.log(sharedId);
|
||||||
|
fetch(PayPalCommerceGatewayOnboarding.endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
authCode: authCode,
|
||||||
|
sharedId: sharedId,
|
||||||
|
nonce: PayPalCommerceGatewayOnboarding.nonce
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
const bootstrap = () => {
|
||||||
|
if (!typeof (PayPalCommerceGatewayOnboarding)) {
|
||||||
|
console.error('PayPal onboarding could not be configured.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.innerText = PayPalCommerceGatewayOnboarding.link.text;
|
||||||
|
|
||||||
|
Object.entries(PayPalCommerceGatewayOnboarding.link.attributes).forEach(
|
||||||
|
(keyValue) => {
|
||||||
|
link.setAttribute(keyValue[0], keyValue[1]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
link.setAttribute('data-paypal-onboard-complete', 'onboardingCallback');
|
||||||
|
|
||||||
|
const table = document.querySelector('.form-table');
|
||||||
|
const wrapper = table.parentNode;
|
||||||
|
//wrapper.insertBefore(link, table);
|
||||||
|
|
||||||
|
const script = document.createElement('script');
|
||||||
|
Object.entries(PayPalCommerceGatewayOnboarding.script.attributes).forEach(
|
||||||
|
(keyValue) => {
|
||||||
|
script.setAttribute(keyValue[0], keyValue[1]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
wrapper.insertBefore(script, table);
|
||||||
|
|
||||||
|
};
|
||||||
|
bootstrap();
|
||||||
|
|
||||||
|
*/
|
File diff suppressed because one or more lines are too long
27
modules.local/ppcp-onboarding/composer.json
Normal file
27
modules.local/ppcp-onboarding/composer.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "inpsyde/ppcp-onboarding",
|
||||||
|
"type": "inpsyde-module",
|
||||||
|
"require": {
|
||||||
|
"dhii/module-interface": "0.2.x-dev"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Inpsyde\\PayPalCommerce\\Onboarding\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Inpsyde\\PayPalCommerce\\Onboarding\\": "tests/PHPUnit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"unit": "./vendor/bin/phpunit --coverage-html build/coverage-report"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.1",
|
||||||
|
"brain/monkey": "^2.4",
|
||||||
|
"inpsyde/php-coding-standards": "^1"
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
9
modules.local/ppcp-onboarding/extensions.php
Normal file
9
modules.local/ppcp-onboarding/extensions.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
11
modules.local/ppcp-onboarding/module.php
Normal file
11
modules.local/ppcp-onboarding/module.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding;
|
||||||
|
|
||||||
|
use Dhii\Modular\Module\ModuleInterface;
|
||||||
|
|
||||||
|
return static function (): ModuleInterface {
|
||||||
|
return new OnboardingModule();
|
||||||
|
};
|
6519
modules.local/ppcp-onboarding/package-lock.json
generated
Normal file
6519
modules.local/ppcp-onboarding/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
23
modules.local/ppcp-onboarding/package.json
Normal file
23
modules.local/ppcp-onboarding/package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "ppc-button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"main": "resources/js/button.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.9.0",
|
||||||
|
"@babel/preset-env": "^7.9.5",
|
||||||
|
"babel-loader": "^8.1.0",
|
||||||
|
"cross-env": "^5.0.1",
|
||||||
|
"file-loader": "^4.2.0",
|
||||||
|
"node-sass": "^4.13.0",
|
||||||
|
"sass-loader": "^8.0.0",
|
||||||
|
"webpack": "^4.42.1",
|
||||||
|
"webpack-cli": "^3.1.2",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.26.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||||
|
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||||
|
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||||
|
}
|
||||||
|
}
|
20
modules.local/ppcp-onboarding/phpunit.xml.dist
Normal file
20
modules.local/ppcp-onboarding/phpunit.xml.dist
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
backupGlobals="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
>
|
||||||
|
<filter>
|
||||||
|
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit">
|
||||||
|
<directory suffix="Test.php">./tests/PHPUnit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
53
modules.local/ppcp-onboarding/resources/js/onboarding.js
Normal file
53
modules.local/ppcp-onboarding/resources/js/onboarding.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
const bootstrap = () => {
|
||||||
|
|
||||||
|
if (!typeof (PayPalCommerceGatewayOnboarding)) {
|
||||||
|
console.error('PayPal onboarding could not be configured.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.innerText = PayPalCommerceGatewayOnboarding.link.text;
|
||||||
|
|
||||||
|
Object.entries(PayPalCommerceGatewayOnboarding.link.attributes).forEach(
|
||||||
|
(keyValue) => {
|
||||||
|
link.setAttribute(keyValue[0], keyValue[1]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const onboardingCallback = (authCode, sharedId) => {
|
||||||
|
console.log(authCode);
|
||||||
|
console.log(sharedId);
|
||||||
|
fetch(PayPalCommerceGatewayOnboarding.endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
authCode: authCode,
|
||||||
|
sharedId: sharedId,
|
||||||
|
nonce: PayPalCommerceGatewayOnboarding.nonce
|
||||||
|
})
|
||||||
|
}).then(function(res) {
|
||||||
|
if (!response.ok) {
|
||||||
|
alert("Something went wrong!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
link.setAttribute('data-paypal-onboard-complete', onboardingCallback);
|
||||||
|
|
||||||
|
const table = document.querySelector('.form-table');
|
||||||
|
const wrapper = table.parentNode;
|
||||||
|
wrapper.insertBefore(link, table);
|
||||||
|
|
||||||
|
const script = document.createElement('script');
|
||||||
|
|
||||||
|
Object.entries(PayPalCommerceGatewayOnboarding.script.attributes).forEach(
|
||||||
|
(keyValue) => {
|
||||||
|
script.setAttribute(keyValue[0], keyValue[1]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
document.body.append(script);
|
||||||
|
|
||||||
|
};
|
||||||
|
bootstrap();
|
72
modules.local/ppcp-onboarding/services.php
Normal file
72
modules.local/ppcp-onboarding/services.php
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding;
|
||||||
|
|
||||||
|
use Dhii\Data\Container\ContainerInterface;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Assets\OnboardingAssets;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Admin\OrderDetail;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Gateway\WcGateway;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Gateway\WcGatewayBase;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\SettingsFields;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'onboarding.state' => function(ContainerInterface $container) : State {
|
||||||
|
$environment = $container->get('onboarding.environment');
|
||||||
|
$settings = $container->get('wcgateway.settings');
|
||||||
|
return new State($environment, $settings);
|
||||||
|
},
|
||||||
|
'onboarding.environment' => function(ContainerInterface $container) : Environment {
|
||||||
|
return new Environment();
|
||||||
|
},
|
||||||
|
|
||||||
|
'onboarding.assets' => function(ContainerInterface $container) : OnboardingAssets {
|
||||||
|
$state = $container->get('onboarding.state');
|
||||||
|
$loginSellerEndpoint = $container->get('onboarding.endpoint.login-seller');
|
||||||
|
return new OnboardingAssets(
|
||||||
|
$container->get('onboarding.url'),
|
||||||
|
$state,
|
||||||
|
$loginSellerEndpoint
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
'onboarding.url' => static function (ContainerInterface $container): string {
|
||||||
|
return plugins_url(
|
||||||
|
'/modules/ppcp-onboarding/',
|
||||||
|
dirname(__FILE__, 3) . '/woocommerce-paypal-commerce-gateway.php'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
'onboarding.endpoint.login-seller' => static function (ContainerInterface $container) : LoginSellerEndpoint {
|
||||||
|
|
||||||
|
$requestData = $container->get('button.request-data');
|
||||||
|
$loginSellerEndpoint = $container->get('api.endpoint.login-seller');
|
||||||
|
$partnerReferralsData = $container->get('api.repository.partner-referrals-data');
|
||||||
|
$gateway = $container->get('wcgateway.gateway.base');
|
||||||
|
return new LoginSellerEndpoint(
|
||||||
|
$requestData,
|
||||||
|
$loginSellerEndpoint,
|
||||||
|
$partnerReferralsData,
|
||||||
|
$gateway
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'onboarding.render' => static function (ContainerInterface $container) : OnboardingRenderer {
|
||||||
|
|
||||||
|
$partnerReferrals = $container->get('api.endpoint.partner-referrals');
|
||||||
|
return new OnboardingRenderer(
|
||||||
|
$partnerReferrals,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding\Assets;
|
||||||
|
|
||||||
|
|
||||||
|
use Inpsyde\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\State;
|
||||||
|
|
||||||
|
class OnboardingAssets
|
||||||
|
{
|
||||||
|
|
||||||
|
private $moduleUrl;
|
||||||
|
private $state;
|
||||||
|
private $loginSellerEndpoint;
|
||||||
|
public function __construct(
|
||||||
|
string $moduleUrl,
|
||||||
|
State $state,
|
||||||
|
LoginSellerEndpoint $loginSellerEndpoint
|
||||||
|
) {
|
||||||
|
$this->moduleUrl = $moduleUrl;
|
||||||
|
$this->state = $state;
|
||||||
|
$this->loginSellerEndpoint = $loginSellerEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register() : bool
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!$this->shouldRender()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->moduleUrl . '/assets/js/onboarding.js';
|
||||||
|
wp_register_script(
|
||||||
|
'ppcp-onboarding',
|
||||||
|
$url,
|
||||||
|
['jquery'],
|
||||||
|
1,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
wp_localize_script(
|
||||||
|
'ppcp-onboarding',
|
||||||
|
'PayPalCommerceGatewayOnboarding',
|
||||||
|
[
|
||||||
|
'endpoint' => home_url(\WC_AJAX::get_endpoint(LoginSellerEndpoint::ENDPOINT)),
|
||||||
|
'nonce' => wp_create_nonce($this->loginSellerEndpoint::nonce()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue() : bool {
|
||||||
|
|
||||||
|
if (! $this->shouldRender()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_enqueue_script('ppcp-onboarding');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function shouldRender() : bool {
|
||||||
|
global $current_section;
|
||||||
|
if ($current_section !== 'ppcp-gateway') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$shouldRender = $this->state->currentState() === State::STATE_PROGRESSIVE;
|
||||||
|
return $shouldRender;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding\Endpoint;
|
||||||
|
|
||||||
|
|
||||||
|
use Inpsyde\PayPalCommerce\ApiClient\Endpoint\LoginSeller;
|
||||||
|
use Inpsyde\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
|
||||||
|
use Inpsyde\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||||
|
use Inpsyde\PayPalCommerce\Button\Endpoint\RequestData;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Gateway\WcGatewayInterface;
|
||||||
|
|
||||||
|
class LoginSellerEndpoint implements EndpointInterface
|
||||||
|
{
|
||||||
|
public const ENDPOINT = 'ppc-login-seller';
|
||||||
|
|
||||||
|
private $requestData;
|
||||||
|
private $loginSellerEndpoint;
|
||||||
|
private $partnerReferralsData;
|
||||||
|
private $gateway;
|
||||||
|
public function __construct(
|
||||||
|
RequestData $requestData,
|
||||||
|
LoginSeller $loginSellerEndpoint,
|
||||||
|
PartnerReferralsData $partnerReferralsData,
|
||||||
|
\WC_Payment_Gateway $gateway
|
||||||
|
) {
|
||||||
|
$this->requestData = $requestData;
|
||||||
|
$this->loginSellerEndpoint = $loginSellerEndpoint;
|
||||||
|
$this->partnerReferralsData = $partnerReferralsData;
|
||||||
|
$this->gateway = $gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function nonce(): string
|
||||||
|
{
|
||||||
|
return self::ENDPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequest(): bool
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
$data = $this->requestData->readRequest($this->nonce());
|
||||||
|
$credentials = $this->loginSellerEndpoint->credentialsFor(
|
||||||
|
$data['sharedId'],
|
||||||
|
$data['authCode'],
|
||||||
|
$this->partnerReferralsData->nonce()
|
||||||
|
);
|
||||||
|
$this->gateway->update_option('client_secret', $credentials->client_secret);
|
||||||
|
$this->gateway->update_option('client_id', $credentials->client_id);
|
||||||
|
wp_send_json_success();
|
||||||
|
return true;
|
||||||
|
} catch (\RuntimeException $error) {
|
||||||
|
wp_send_json_error($error->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
modules.local/ppcp-onboarding/src/Environment.php
Normal file
36
modules.local/ppcp-onboarding/src/Environment.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding;
|
||||||
|
|
||||||
|
|
||||||
|
class Environment
|
||||||
|
{
|
||||||
|
|
||||||
|
public const PRODUCTION = 'production';
|
||||||
|
public const SANDBOX = 'sandbox';
|
||||||
|
public const VALID_ENVIRONMENTS = [
|
||||||
|
self::PRODUCTION,
|
||||||
|
self::SANDBOX,
|
||||||
|
];
|
||||||
|
|
||||||
|
public const OPTION_KEY = 'ppcp-env';
|
||||||
|
|
||||||
|
public function currentEnvironment() : string
|
||||||
|
{
|
||||||
|
return (string) get_option(self::OPTION_KEY, self::PRODUCTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentEnvironmentIs(string $environment) : bool {
|
||||||
|
return $this->currentEnvironment() === $environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeEnvironmentTo(string $environment) : bool
|
||||||
|
{
|
||||||
|
if (! in_array($environment, self::VALID_ENVIRONMENTS, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
update_option(self::OPTION_KEY, $environment);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
58
modules.local/ppcp-onboarding/src/OnboardingModule.php
Normal file
58
modules.local/ppcp-onboarding/src/OnboardingModule.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding;
|
||||||
|
|
||||||
|
use Dhii\Container\ServiceProvider;
|
||||||
|
use Dhii\Modular\Module\ModuleInterface;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Assets\OnboardingAssets;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Endpoint\LoginSellerEndpoint;
|
||||||
|
use Interop\Container\ServiceProviderInterface;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class OnboardingModule implements ModuleInterface
|
||||||
|
{
|
||||||
|
public function setup(): ServiceProviderInterface
|
||||||
|
{
|
||||||
|
return new ServiceProvider(
|
||||||
|
require __DIR__ . '/../services.php',
|
||||||
|
require __DIR__ . '/../extensions.php'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
|
||||||
|
$assetLoader = $container->get('onboarding.assets');
|
||||||
|
/**
|
||||||
|
* @var OnboardingAssets $assetLoader
|
||||||
|
*/
|
||||||
|
add_action(
|
||||||
|
'admin_enqueue_scripts',
|
||||||
|
[
|
||||||
|
$assetLoader,
|
||||||
|
'register',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
add_action(
|
||||||
|
'woocommerce_settings_checkout',
|
||||||
|
[
|
||||||
|
$assetLoader,
|
||||||
|
'enqueue',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
add_action(
|
||||||
|
'wc_ajax_' . LoginSellerEndpoint::ENDPOINT,
|
||||||
|
static function () use ($container) {
|
||||||
|
$endpoint = $container->get('onboarding.endpoint.login-seller');
|
||||||
|
/**
|
||||||
|
* @var ChangeCartEndpoint $endpoint
|
||||||
|
*/
|
||||||
|
$endpoint->handleRequest();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding\Render;
|
||||||
|
|
||||||
|
|
||||||
|
use Inpsyde\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals;
|
||||||
|
|
||||||
|
class OnboardingRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
private $partnerReferrals;
|
||||||
|
public function __construct(PartnerReferrals $partnerReferrals)
|
||||||
|
{
|
||||||
|
$this->partnerReferrals = $partnerReferrals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render() {
|
||||||
|
$url = add_query_arg(
|
||||||
|
[
|
||||||
|
'displayMode'=>'minibrowser',
|
||||||
|
],
|
||||||
|
$this->partnerReferrals->getSignupLink()
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
function onboardedCallback(authCode, sharedId) {
|
||||||
|
onboardingCallback(authCode, sharedId);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<a target="_blank" data-paypal-onboard-complete="onboardedCallback" href="<?php echo $url; ?>" data-paypal-button="true">Sign up for PayPal</a>
|
||||||
|
<script id="paypal-js" src="https://www.sandbox.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"></script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
43
modules.local/ppcp-onboarding/src/State.php
Normal file
43
modules.local/ppcp-onboarding/src/State.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\Onboarding;
|
||||||
|
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class State
|
||||||
|
{
|
||||||
|
const STATE_START = 0;
|
||||||
|
const STATE_PROGRESSIVE = 4;
|
||||||
|
const STATE_ONBOARDED = 8;
|
||||||
|
|
||||||
|
private $environment;
|
||||||
|
private $settings;
|
||||||
|
public function __construct(
|
||||||
|
Environment $environment,
|
||||||
|
ContainerInterface $settings
|
||||||
|
) {
|
||||||
|
$this->environment = $environment;
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentState() : int {
|
||||||
|
$value = self::STATE_START;
|
||||||
|
/**
|
||||||
|
* Having provided the merchant email means, we are at least
|
||||||
|
* in the progressive phase of our onboarding.
|
||||||
|
**/
|
||||||
|
if ($this->settings->has('merchant_email') && is_email($this->settings->get('merchant_email'))) {
|
||||||
|
$value = self::STATE_PROGRESSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once we can fetch credentials we are completely onboarded.
|
||||||
|
*/
|
||||||
|
if ($this->settings->has('client_id')) {
|
||||||
|
$value = self::STATE_ONBOARDED;
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
35
modules.local/ppcp-onboarding/webpack.config.js
Normal file
35
modules.local/ppcp-onboarding/webpack.config.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
const path = require('path');
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
devtool: 'sourcemap',
|
||||||
|
mode: isProduction ? 'production' : 'development',
|
||||||
|
target: 'web',
|
||||||
|
entry: {
|
||||||
|
onboarding: path.resolve('./resources/js/onboarding.js'),
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'assets/'),
|
||||||
|
filename: 'js/[name].js',
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [{
|
||||||
|
test: /\.js?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
name: 'css/[name].css',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{loader:'sass-loader'}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
|
@ -5,5 +5,5 @@ declare(strict_types=1);
|
||||||
namespace Inpsyde\PayPalCommerce\WcGateway;
|
namespace Inpsyde\PayPalCommerce\WcGateway;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Inpsyde\PayPalCommerce\WcGateway;
|
namespace Inpsyde\PayPalCommerce\WcGateway;
|
||||||
|
|
||||||
use Dhii\Data\Container\ContainerInterface;
|
use Dhii\Data\Container\ContainerInterface;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Admin\OrderDetail;
|
use Inpsyde\PayPalCommerce\Onboarding\State;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
|
use Inpsyde\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
|
use Inpsyde\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
use Inpsyde\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
||||||
|
@ -15,8 +15,11 @@ use Inpsyde\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
|
use Inpsyde\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\FullyOnboardedSettings;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\ProgressiveSettings;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Settings\SettingsFields;
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\SettingsFields;
|
||||||
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\StartSettings;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'wcgateway.gateway.base' => static function (ContainerInterface $container): WcGatewayBase {
|
'wcgateway.gateway.base' => static function (ContainerInterface $container): WcGatewayBase {
|
||||||
|
@ -27,11 +30,13 @@ return [
|
||||||
$settingsFields = $container->get('wcgateway.settings.fields');
|
$settingsFields = $container->get('wcgateway.settings.fields');
|
||||||
$authorizedPayments = $container->get('wcgateway.processor.authorized-payments');
|
$authorizedPayments = $container->get('wcgateway.processor.authorized-payments');
|
||||||
$notice = $container->get('wcgateway.notice.authorize-order-action');
|
$notice = $container->get('wcgateway.notice.authorize-order-action');
|
||||||
|
$onboardingRender = $container->get('onboarding.render');
|
||||||
return new WcGateway(
|
return new WcGateway(
|
||||||
$settingsFields,
|
$settingsFields,
|
||||||
$orderProcessor,
|
$orderProcessor,
|
||||||
$authorizedPayments,
|
$authorizedPayments,
|
||||||
$notice
|
$notice,
|
||||||
|
$onboardingRender
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'wcgateway.disabler' => static function (ContainerInterface $container): DisableGateways {
|
'wcgateway.disabler' => static function (ContainerInterface $container): DisableGateways {
|
||||||
|
@ -40,19 +45,29 @@ return [
|
||||||
},
|
},
|
||||||
'wcgateway.settings' => static function (ContainerInterface $container): Settings {
|
'wcgateway.settings' => static function (ContainerInterface $container): Settings {
|
||||||
$gateway = $container->get('wcgateway.gateway.base');
|
$gateway = $container->get('wcgateway.gateway.base');
|
||||||
$settingsField = $container->get('wcgateway.settings.fields');
|
return new Settings($gateway);
|
||||||
return new Settings($gateway, $settingsField);
|
|
||||||
},
|
},
|
||||||
'wcgateway.notice.connect' => static function (ContainerInterface $container): ConnectAdminNotice {
|
'wcgateway.notice.connect' => static function (ContainerInterface $container): ConnectAdminNotice {
|
||||||
|
$state = $container->get('onboarding.state');
|
||||||
$settings = $container->get('wcgateway.settings');
|
$settings = $container->get('wcgateway.settings');
|
||||||
return new ConnectAdminNotice($settings);
|
return new ConnectAdminNotice($state, $settings);
|
||||||
},
|
},
|
||||||
'wcgateway.notice.authorize-order-action' =>
|
'wcgateway.notice.authorize-order-action' =>
|
||||||
static function (ContainerInterface $container): AuthorizeOrderActionNotice {
|
static function (ContainerInterface $container): AuthorizeOrderActionNotice {
|
||||||
return new AuthorizeOrderActionNotice();
|
return new AuthorizeOrderActionNotice();
|
||||||
},
|
},
|
||||||
'wcgateway.settings.fields' => static function (ContainerInterface $container): SettingsFields {
|
'wcgateway.settings.fields' => static function (ContainerInterface $container): SettingsFields {
|
||||||
return new SettingsFields();
|
$state = $container->get('onboarding.state');
|
||||||
|
/**
|
||||||
|
* @var State $state
|
||||||
|
*/
|
||||||
|
if ($state->currentState() === State::STATE_START) {
|
||||||
|
return new StartSettings();
|
||||||
|
}
|
||||||
|
if ($state->currentState() === State::STATE_PROGRESSIVE) {
|
||||||
|
return new ProgressiveSettings();
|
||||||
|
}
|
||||||
|
return new FullyOnboardedSettings();
|
||||||
},
|
},
|
||||||
'wcgateway.order-processor' => static function (ContainerInterface $container): OrderProcessor {
|
'wcgateway.order-processor' => static function (ContainerInterface $container): OrderProcessor {
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class OrderTablePaymentStatusColumn
|
||||||
|
|
||||||
public function register(array $columns): array
|
public function register(array $columns): array
|
||||||
{
|
{
|
||||||
if ($this->settings->get('intent') !== self::INTENT) {
|
if (! $this->settings->has('intent') || $this->settings->get('intent') !== self::INTENT) {
|
||||||
return $columns;
|
return $columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class OrderTablePaymentStatusColumn
|
||||||
|
|
||||||
public function render(string $column, int $wcOrderId)
|
public function render(string $column, int $wcOrderId)
|
||||||
{
|
{
|
||||||
if ($this->settings->get('intent') !== self::INTENT) {
|
if (! $this->settings->has('intent') || $this->settings->get('intent') !== self::INTENT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Inpsyde\PayPalCommerce\ApiClient\Entity\Order;
|
||||||
use Inpsyde\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
use Inpsyde\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||||
use Inpsyde\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
use Inpsyde\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||||
use Inpsyde\PayPalCommerce\ApiClient\Repository\CartRepository;
|
use Inpsyde\PayPalCommerce\ApiClient\Repository\CartRepository;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\Render\OnboardingRenderer;
|
||||||
use Inpsyde\PayPalCommerce\Session\SessionHandler;
|
use Inpsyde\PayPalCommerce\Session\SessionHandler;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
|
use Inpsyde\PayPalCommerce\WcGateway\Notice\AuthorizeOrderActionNotice;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
use Inpsyde\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||||
|
@ -30,18 +31,21 @@ class WcGateway extends WcGatewayBase
|
||||||
private $authorizedPayments;
|
private $authorizedPayments;
|
||||||
private $notice;
|
private $notice;
|
||||||
private $orderProcessor;
|
private $orderProcessor;
|
||||||
|
private $onboardingRenderer;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
SettingsFields $settingsFields,
|
SettingsFields $settingsFields,
|
||||||
OrderProcessor $orderProcessor,
|
OrderProcessor $orderProcessor,
|
||||||
AuthorizedPaymentsProcessor $authorizedPayments,
|
AuthorizedPaymentsProcessor $authorizedPayments,
|
||||||
AuthorizeOrderActionNotice $notice
|
AuthorizeOrderActionNotice $notice,
|
||||||
|
OnboardingRenderer $onboardingRenderer
|
||||||
) {
|
) {
|
||||||
|
|
||||||
$this->orderProcessor = $orderProcessor;
|
$this->orderProcessor = $orderProcessor;
|
||||||
$this->authorizedPayments = $authorizedPayments;
|
$this->authorizedPayments = $authorizedPayments;
|
||||||
$this->notice = $notice;
|
$this->notice = $notice;
|
||||||
$this->settingsFields = $settingsFields;
|
$this->settingsFields = $settingsFields;
|
||||||
|
$this->onboardingRenderer = $onboardingRenderer;
|
||||||
|
|
||||||
$this->method_title = __('PayPal Payments', 'woocommerce-paypal-gateway');
|
$this->method_title = __('PayPal Payments', 'woocommerce-paypal-gateway');
|
||||||
$this->method_description = __(
|
$this->method_description = __(
|
||||||
|
@ -132,4 +136,8 @@ class WcGateway extends WcGatewayBase
|
||||||
$displayMessage = (isset($messageMapping[$status])) ? $messageMapping[$status] : AuthorizeOrderActionNotice::FAILED;
|
$displayMessage = (isset($messageMapping[$status])) ? $messageMapping[$status] : AuthorizeOrderActionNotice::FAILED;
|
||||||
$this->notice->displayMessage($displayMessage);
|
$this->notice->displayMessage($displayMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generate_ppcp_onboarding_html($a, $b) {
|
||||||
|
$this->onboardingRenderer->render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,18 @@ declare(strict_types=1);
|
||||||
namespace Inpsyde\PayPalCommerce\WcGateway\Notice;
|
namespace Inpsyde\PayPalCommerce\WcGateway\Notice;
|
||||||
|
|
||||||
use Inpsyde\PayPalCommerce\AdminNotices\Entity\Message;
|
use Inpsyde\PayPalCommerce\AdminNotices\Entity\Message;
|
||||||
|
use Inpsyde\PayPalCommerce\Onboarding\State;
|
||||||
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
|
use Inpsyde\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
class ConnectAdminNotice
|
class ConnectAdminNotice
|
||||||
{
|
{
|
||||||
|
private $state;
|
||||||
private $settings;
|
private $settings;
|
||||||
|
|
||||||
public function __construct(Settings $settings)
|
public function __construct(State $state, ContainerInterface $settings)
|
||||||
{
|
{
|
||||||
|
$this->state = $state;
|
||||||
$this->settings = $settings;
|
$this->settings = $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +32,7 @@ class ConnectAdminNotice
|
||||||
'%1$s is almost ready. To get started, <a href="%2$s">connect your account</a>.',
|
'%1$s is almost ready. To get started, <a href="%2$s">connect your account</a>.',
|
||||||
'woocommerce-paypal-commerce-gateway'
|
'woocommerce-paypal-commerce-gateway'
|
||||||
),
|
),
|
||||||
$this->settings->get('title'),
|
$this->settings->has('title') ? $this->settings->get('title') : '',
|
||||||
// TODO: find a better way to get the url
|
// TODO: find a better way to get the url
|
||||||
admin_url('admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway')
|
admin_url('admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway')
|
||||||
);
|
);
|
||||||
|
@ -38,6 +42,6 @@ class ConnectAdminNotice
|
||||||
protected function shouldDisplay(): bool
|
protected function shouldDisplay(): bool
|
||||||
{
|
{
|
||||||
// TODO: decide on what condition to display
|
// TODO: decide on what condition to display
|
||||||
return !wc_string_to_bool($this->settings->get('enabled'));
|
return $this->state->currentState() < State::STATE_PROGRESSIVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\WcGateway\Settings;
|
||||||
|
|
||||||
|
|
||||||
|
class FullyOnboardedSettings implements SettingsFields
|
||||||
|
{
|
||||||
|
public function fields(): array
|
||||||
|
{
|
||||||
|
return array_merge(
|
||||||
|
$this->gateway(),
|
||||||
|
$this->account(),
|
||||||
|
$this->buttons(),
|
||||||
|
$this->creditCards(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function gateway(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'enabled' => [
|
||||||
|
'title' => __('Enable/Disable', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Enable PayPal Payments', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'title' => [
|
||||||
|
'title' => __('Title', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'text',
|
||||||
|
'description' => __(
|
||||||
|
'This controls the title which the user sees during checkout.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'default' => __('PayPal', 'woocommerce-paypal-gateway'),
|
||||||
|
'desc_tip' => true,
|
||||||
|
],
|
||||||
|
'description' => [
|
||||||
|
'title' => __('Description', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'text',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'This controls the description which the user sees during checkout.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'default' => __(
|
||||||
|
'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'intent' => [
|
||||||
|
'title' => __('Intent', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'select',
|
||||||
|
'class' => 'wc-enhanced-select',
|
||||||
|
'default' => 'capture',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'The intent to either capture payment immediately or authorize a payment for an order after order creation.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'options' => [
|
||||||
|
'capture' => __('Capture', 'woocommerce-paypal-gateway'),
|
||||||
|
'authorize' => __('Authorize', 'woocommerce-paypal-gateway'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function account(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'account_settings' => [
|
||||||
|
'title' => __('Account Settings', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'title',
|
||||||
|
'description' => '',
|
||||||
|
],
|
||||||
|
'sandbox_on' => [
|
||||||
|
'title' => __('Enable Sandbox', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __(
|
||||||
|
'For testing your integration, you can enable the sandbox.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buttons(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'button_settings' => [
|
||||||
|
'title' => __('SmartButton Settings', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'title',
|
||||||
|
'description' => __(
|
||||||
|
'Customize the appearance of PayPal Payments on your site.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'button_single_product_enabled' => [
|
||||||
|
'title' => __('Buttons on Single Product', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Enable on Single Product', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'button_mini_cart_enabled' => [
|
||||||
|
'title' => __('Buttons on Mini Cart', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Enable on Mini Cart', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'button_cart_enabled' => [
|
||||||
|
'title' => __('Buttons on Cart', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Enable on Cart', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'button_color' => [
|
||||||
|
'title' => __('Color', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'select',
|
||||||
|
'class' => 'wc-enhanced-select',
|
||||||
|
'default' => 'gold',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'Controls the background color of the primary button. Use "Gold" to leverage PayPal\'s recognition and preference, or change it to match your site design or aesthetic.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'options' => [
|
||||||
|
'gold' => __('Gold (Recommended)', 'woocommerce-paypal-gateway'),
|
||||||
|
'blue' => __('Blue', 'woocommerce-paypal-gateway'),
|
||||||
|
'silver' => __('Silver', 'woocommerce-paypal-gateway'),
|
||||||
|
'black' => __('Black', 'woocommerce-paypal-gateway'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'button_shape' => [
|
||||||
|
'title' => __('Shape', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'select',
|
||||||
|
'class' => 'wc-enhanced-select',
|
||||||
|
'default' => 'rect',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'The pill-shaped button\'s unique and powerful shape signifies PayPal in people\'s minds. Use the rectangular button as an alternative when pill-shaped buttons might pose design challenges.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'options' => [
|
||||||
|
'pill' => __('Pill', 'woocommerce-paypal-gateway'),
|
||||||
|
'rect' => __('Rectangle', 'woocommerce-paypal-gateway'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'disable_funding' => [
|
||||||
|
'title' => __('Disable funding sources', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'multiselect',
|
||||||
|
'class' => 'wc-enhanced-select',
|
||||||
|
'default' => [],
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'By default all possible funding sources will be shown. You can disable some sources, if you wish.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'options' => [
|
||||||
|
'card' => _x('Credit or debit cards', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'credit' => _x('PayPal Credit', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'venmo' => _x('Venmo', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'sepa' => _x('SEPA-Lastschrift', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'bancontact' => _x('Bancontact', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'eps' => _x('eps', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'giropay' => _x('giropay', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'ideal' => _x('iDEAL', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'mybank' => _x('MyBank', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'p24' => _x('Przelewy24', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
'sofort' => _x('Sofort', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function creditCards() : array {
|
||||||
|
return [
|
||||||
|
|
||||||
|
'credit_card_settings' => [
|
||||||
|
'title' => __('Credit Card Settings', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'title',
|
||||||
|
'description' => __(
|
||||||
|
'Customize the appearance of Credit Card Payments on your site.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'dcc_cart_enabled' => [
|
||||||
|
'title' => __('Enable credit card on cart', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Allow your customers to pay with credit card directly in your cart.', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'dcc_mini_cart_enabled' => [
|
||||||
|
'title' => __('Enable credit card on mini cart', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Allow your customers to pay with credit card directly in your mini cart.', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'dcc_checkout_enabled' => [
|
||||||
|
'title' => __('Enable credit card on checkout', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Allow your customers to pay with credit card in the checkout.', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'dcc_single_product_enabled' => [
|
||||||
|
'title' => __('Enable credit card on products', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __('Allow your customers to pay with credit card instantly on the product page.', 'woocommerce-paypal-gateway'),
|
||||||
|
'default' => 'yes',
|
||||||
|
],
|
||||||
|
'disable_cards' => [
|
||||||
|
'title' => __('Disable specific credid cards', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'multiselect',
|
||||||
|
'class' => 'wc-enhanced-select',
|
||||||
|
'default' => [],
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => __(
|
||||||
|
'By default all possible credit cards will be shown. You can disable some cards, if you wish.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'options' => [
|
||||||
|
'visa' => _x('Visa', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
'mastercard' => _x('Mastercard', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
'amex' => _x('American Express', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
'discover' => _x('Discover', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
'jcb' => _x('JCB', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
'elo' => _x('Elo', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
'hiper' => _x('Hiper', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\WcGateway\Settings;
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressiveSettings extends StartSettings implements SettingsFields
|
||||||
|
{
|
||||||
|
|
||||||
|
public function fields(): array
|
||||||
|
{
|
||||||
|
$fields = parent::fields();
|
||||||
|
$fields[] = [
|
||||||
|
'type' => 'ppcp_onboarding',
|
||||||
|
];
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,12 +11,10 @@ use Psr\Container\ContainerInterface;
|
||||||
class Settings implements ContainerInterface
|
class Settings implements ContainerInterface
|
||||||
{
|
{
|
||||||
private $gateway;
|
private $gateway;
|
||||||
private $formFields;
|
|
||||||
|
|
||||||
public function __construct(WcGatewayInterface $gateway, SettingsFields $formFields)
|
public function __construct(WcGatewayInterface $gateway)
|
||||||
{
|
{
|
||||||
$this->gateway = $gateway;
|
$this->gateway = $gateway;
|
||||||
$this->formFields = $formFields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration.NoReturnType
|
// phpcs:disable Inpsyde.CodeQuality.ReturnTypeDeclaration.NoReturnType
|
||||||
|
@ -31,6 +29,6 @@ class Settings implements ContainerInterface
|
||||||
|
|
||||||
public function has($id)
|
public function has($id)
|
||||||
{
|
{
|
||||||
return array_key_exists($id, $this->formFields->fields());
|
return !!$this->gateway->get_option($id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,231 +6,7 @@ namespace Inpsyde\PayPalCommerce\WcGateway\Settings;
|
||||||
|
|
||||||
//phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong
|
//phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong
|
||||||
|
|
||||||
class SettingsFields
|
interface SettingsFields
|
||||||
{
|
{
|
||||||
public function fields(): array
|
public function fields(): array;
|
||||||
{
|
|
||||||
return array_merge(
|
|
||||||
$this->gateway(),
|
|
||||||
$this->account(),
|
|
||||||
$this->buttons(),
|
|
||||||
$this->creditCards(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function gateway(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'enabled' => [
|
|
||||||
'title' => __('Enable/Disable', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Enable PayPal Payments', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'title' => [
|
|
||||||
'title' => __('Title', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'text',
|
|
||||||
'description' => __(
|
|
||||||
'This controls the title which the user sees during checkout.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'default' => __('PayPal', 'woocommerce-paypal-gateway'),
|
|
||||||
'desc_tip' => true,
|
|
||||||
],
|
|
||||||
'description' => [
|
|
||||||
'title' => __('Description', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'text',
|
|
||||||
'desc_tip' => true,
|
|
||||||
'description' => __(
|
|
||||||
'This controls the description which the user sees during checkout.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'default' => __(
|
|
||||||
'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
'intent' => [
|
|
||||||
'title' => __('Intent', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'select',
|
|
||||||
'class' => 'wc-enhanced-select',
|
|
||||||
'default' => 'capture',
|
|
||||||
'desc_tip' => true,
|
|
||||||
'description' => __(
|
|
||||||
'The intent to either capture payment immediately or authorize a payment for an order after order creation.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'options' => [
|
|
||||||
'capture' => __('Capture', 'woocommerce-paypal-gateway'),
|
|
||||||
'authorize' => __('Authorize', 'woocommerce-paypal-gateway'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function account(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'account_settings' => [
|
|
||||||
'title' => __('Account Settings', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'title',
|
|
||||||
'description' => '',
|
|
||||||
],
|
|
||||||
'sandbox_on' => [
|
|
||||||
'title' => __('Enable Sandbox', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __(
|
|
||||||
'For testing your integration, you can enable the sandbox.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buttons(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'button_settings' => [
|
|
||||||
'title' => __('SmartButton Settings', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'title',
|
|
||||||
'description' => __(
|
|
||||||
'Customize the appearance of PayPal Payments on your site.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
'button_single_product_enabled' => [
|
|
||||||
'title' => __('Buttons on Single Product', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Enable on Single Product', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'button_mini_cart_enabled' => [
|
|
||||||
'title' => __('Buttons on Mini Cart', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Enable on Mini Cart', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'button_cart_enabled' => [
|
|
||||||
'title' => __('Buttons on Cart', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Enable on Cart', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'button_color' => [
|
|
||||||
'title' => __('Color', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'select',
|
|
||||||
'class' => 'wc-enhanced-select',
|
|
||||||
'default' => 'gold',
|
|
||||||
'desc_tip' => true,
|
|
||||||
'description' => __(
|
|
||||||
'Controls the background color of the primary button. Use "Gold" to leverage PayPal\'s recognition and preference, or change it to match your site design or aesthetic.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'options' => [
|
|
||||||
'gold' => __('Gold (Recommended)', 'woocommerce-paypal-gateway'),
|
|
||||||
'blue' => __('Blue', 'woocommerce-paypal-gateway'),
|
|
||||||
'silver' => __('Silver', 'woocommerce-paypal-gateway'),
|
|
||||||
'black' => __('Black', 'woocommerce-paypal-gateway'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'button_shape' => [
|
|
||||||
'title' => __('Shape', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'select',
|
|
||||||
'class' => 'wc-enhanced-select',
|
|
||||||
'default' => 'rect',
|
|
||||||
'desc_tip' => true,
|
|
||||||
'description' => __(
|
|
||||||
'The pill-shaped button\'s unique and powerful shape signifies PayPal in people\'s minds. Use the rectangular button as an alternative when pill-shaped buttons might pose design challenges.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'options' => [
|
|
||||||
'pill' => __('Pill', 'woocommerce-paypal-gateway'),
|
|
||||||
'rect' => __('Rectangle', 'woocommerce-paypal-gateway'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'disable_funding' => [
|
|
||||||
'title' => __('Disable funding sources', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'multiselect',
|
|
||||||
'class' => 'wc-enhanced-select',
|
|
||||||
'default' => [],
|
|
||||||
'desc_tip' => true,
|
|
||||||
'description' => __(
|
|
||||||
'By default all possible funding sources will be shown. You can disable some sources, if you wish.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'options' => [
|
|
||||||
'card' => _x('Credit or debit cards', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'credit' => _x('PayPal Credit', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'venmo' => _x('Venmo', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'sepa' => _x('SEPA-Lastschrift', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'bancontact' => _x('Bancontact', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'eps' => _x('eps', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'giropay' => _x('giropay', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'ideal' => _x('iDEAL', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'mybank' => _x('MyBank', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'p24' => _x('Przelewy24', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
'sofort' => _x('Sofort', 'Name of payment method', 'woocommerce-paypal-gateway'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function creditCards() : array {
|
|
||||||
return [
|
|
||||||
|
|
||||||
'credit_card_settings' => [
|
|
||||||
'title' => __('Credit Card Settings', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'title',
|
|
||||||
'description' => __(
|
|
||||||
'Customize the appearance of Credit Card Payments on your site.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
'dcc_cart_enabled' => [
|
|
||||||
'title' => __('Enable credit card on cart', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Allow your customers to pay with credit card directly in your cart.', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'dcc_mini_cart_enabled' => [
|
|
||||||
'title' => __('Enable credit card on mini cart', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Allow your customers to pay with credit card directly in your mini cart.', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'dcc_checkout_enabled' => [
|
|
||||||
'title' => __('Enable credit card on checkout', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Allow your customers to pay with credit card in the checkout.', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'dcc_single_product_enabled' => [
|
|
||||||
'title' => __('Enable credit card on products', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'label' => __('Allow your customers to pay with credit card instantly on the product page.', 'woocommerce-paypal-gateway'),
|
|
||||||
'default' => 'yes',
|
|
||||||
],
|
|
||||||
'disable_cards' => [
|
|
||||||
'title' => __('Disable specific credid cards', 'woocommerce-paypal-gateway'),
|
|
||||||
'type' => 'multiselect',
|
|
||||||
'class' => 'wc-enhanced-select',
|
|
||||||
'default' => [],
|
|
||||||
'desc_tip' => true,
|
|
||||||
'description' => __(
|
|
||||||
'By default all possible credit cards will be shown. You can disable some cards, if you wish.',
|
|
||||||
'woocommerce-paypal-gateway'
|
|
||||||
),
|
|
||||||
'options' => [
|
|
||||||
'visa' => _x('Visa', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
'mastercard' => _x('Mastercard', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
'amex' => _x('American Express', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
'discover' => _x('Discover', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
'jcb' => _x('JCB', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
'elo' => _x('Elo', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
'hiper' => _x('Hiper', 'Name of credit card', 'woocommerce-paypal-gateway'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
24
modules.local/ppcp-wc-gateway/src/Settings/StartSettings.php
Normal file
24
modules.local/ppcp-wc-gateway/src/Settings/StartSettings.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Inpsyde\PayPalCommerce\WcGateway\Settings;
|
||||||
|
|
||||||
|
|
||||||
|
class StartSettings implements SettingsFields
|
||||||
|
{
|
||||||
|
|
||||||
|
public function fields(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'merchant_email' => [
|
||||||
|
'title' => __('Email', 'woocommerce-paypal-gateway'),
|
||||||
|
'type' => 'email',
|
||||||
|
'description' => __(
|
||||||
|
'Please enter the email address with which you want to receive payments.',
|
||||||
|
'woocommerce-paypal-gateway'
|
||||||
|
),
|
||||||
|
'default' => '',
|
||||||
|
'desc_tip' => true,
|
||||||
|
],];
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ declare( strict_types = 1 );
|
||||||
* Network: TODO: Specify value 'true' or remove line
|
* Network: TODO: Specify value 'true' or remove line
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace Inpsyde\PayPalCommerce;
|
namespace Inpsyde\PayPalCommerce;
|
||||||
|
|
||||||
use Dhii\Container\CachingContainer;
|
use Dhii\Container\CachingContainer;
|
||||||
|
@ -38,6 +39,9 @@ use Inpsyde\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||||
foreach (glob(plugin_dir_path(__FILE__).'modules/*/module.php') as $moduleFile) {
|
foreach (glob(plugin_dir_path(__FILE__).'modules/*/module.php') as $moduleFile) {
|
||||||
$modules[] = (@require $moduleFile)();
|
$modules[] = (@require $moduleFile)();
|
||||||
}
|
}
|
||||||
|
foreach (glob(plugin_dir_path(__FILE__).'modules.local/*/module.php') as $moduleFile) {
|
||||||
|
$modules[] = (@require $moduleFile)();
|
||||||
|
}
|
||||||
$providers = [];
|
$providers = [];
|
||||||
foreach ($modules as $module) {
|
foreach ($modules as $module) {
|
||||||
/* @var $module ModuleInterface */
|
/* @var $module ModuleInterface */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue