1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-11 05:32:39 +01:00

Merge pull request #6456 from turbo124/square

Square
This commit is contained in:
David Bomba 2021-08-14 22:17:57 +10:00 committed by GitHub
commit 398382fb69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 857 additions and 7 deletions

View File

@ -787,6 +787,27 @@ class CreateSingleAccount extends Command
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.square') && ($this->gateway == 'all' || $this->gateway == 'square')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = '65faab2ab6e3223dbe848b1686490baz';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.square'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
}
private function createRecurringInvoice($client)

View File

@ -81,9 +81,13 @@ class Gateway extends StaticModel
case 1:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]];//Authorize.net
break;
case 1:
case 11:
return [GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false]];//Payfast
break;
case 7:
return [
GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => true], // Mollie
];
case 15:
return [GatewayType::PAYPAL => ['refund' => true, 'token_billing' => false]]; //Paypal
break;
@ -110,11 +114,12 @@ class Gateway extends StaticModel
GatewayType::PAYPAL => ['refund' => true, 'token_billing' => true]
];
break;
case 7:
case 57:
return [
GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => true], // Mollie
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], //Square
];
break;
break;
default:
return [];
break;

View File

@ -70,7 +70,8 @@ class SystemLog extends Model
const TYPE_PAYFAST = 310;
const TYPE_PAYTRACE = 311;
const TYPE_MOLLIE = 312;
const TYPE_SQUARE = 320;
const TYPE_QUOTA_EXCEEDED = 400;
const TYPE_UPSTREAM_FAILURE = 401;

View File

@ -0,0 +1,328 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\Square;
use App\Exceptions\PaymentFailed;
use App\Jobs\Mail\PaymentFailureMailer;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\SquarePaymentDriver;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class CreditCard
{
use MakesHash;
public $square_driver;
public function __construct(SquarePaymentDriver $square_driver)
{
$this->square_driver = $square_driver;
$this->square_driver->init();
}
public function authorizeView($data)
{
$data['gateway'] = $this->square_driver;
return render('gateways.square.credit_card.authorize', $data);
}
public function authorizeResponse($request)
{
$payment = false;
$amount_money = new \Square\Models\Money();
$amount_money->setAmount(100); //amount in cents
$amount_money->setCurrency($this->square_driver->client->currency()->code);
$body = new \Square\Models\CreatePaymentRequest(
$request->sourceId,
Str::random(32),
$amount_money
);
$body->setAutocomplete(false);
$body->setLocationId($this->square_driver->company_gateway->getConfigField('locationId'));
$body->setReferenceId(Str::random(16));
$api_response = $this->square_driver->square->getPaymentsApi()->createPayment($body);
if ($api_response->isSuccess()) {
// $result = $api_response->getResult();
$result = $api_response->getBody();
$payment = json_decode($result);
} else {
$errors = $api_response->getErrors();
nlog($errors);
}
/*
Success response looks like this:
{
"payment": {
"id": "Dv9xlBgSgVB8i6eT0imRYFjcrOaZY",
"created_at": "2021-03-31T20:56:13.220Z",
"updated_at": "2021-03-31T20:56:13.411Z",
"amount_money": {
"amount": 100,
"currency": "USD"
},
"status": "COMPLETED",
"delay_duration": "PT168H",
"source_type": "CARD",
"card_details": {
"status": "CAPTURED",
"card": {
"card_brand": "AMERICAN_EXPRESS",
"last_4": "6550",
"exp_month": 3,
"exp_year": 2023,
"fingerprint": "sq-1-hPdOWUYtEMft3yQ",
"card_type": "CREDIT",
"prepaid_type": "NOT_PREPAID",
"bin": "371263"
},
"entry_method": "KEYED",
"cvv_status": "CVV_ACCEPTED",
"avs_status": "AVS_ACCEPTED",
"statement_description": "SQ *DEFAULT TEST ACCOUNT",
"card_payment_timeline": {
"authorized_at": "2021-03-31T20:56:13.334Z",
"captured_at": "2021-03-31T20:56:13.411Z"
}
},
"location_id": "VJN4XSBFTVPK9",
"total_money": {
"amount": 100,
"currency": "USD"
},
"approved_money": {
"amount": 100,
"currency": "USD"
}
}
}
*/
$billing_address = new \Square\Models\Address();
$billing_address->setAddressLine1($this->square_driver->client->address1);
$billing_address->setAddressLine2($this->square_driver->client->address2);
$billing_address->setLocality($this->square_driver->client->city);
$billing_address->setAdministrativeDistrictLevel1($this->square_driver->client->state);
$billing_address->setPostalCode($this->square_driver->client->postal_code);
$billing_address->setCountry($this->square_driver->client->country->iso_3166_2);
$body = new \Square\Models\CreateCustomerRequest();
$body->setGivenName($this->square_driver->client->present()->name());
$body->setFamilyName('');
$body->setEmailAddress($this->square_driver->client->present()->email());
$body->setAddress($billing_address);
$body->setPhoneNumber($this->square_driver->client->phone);
$body->setReferenceId($this->square_driver->client->number);
$body->setNote('Created by Invoice Ninja.');
$api_response = $this->square_driver
->square
->getCustomersApi()
->createCustomer($body);
if ($api_response->isSuccess()) {
$result = $api_response->getResult();
nlog($result);
} else {
$errors = $api_response->getErrors();
nlog($errors);
}
/*Customer now created response
{
"customer": {
"id": "Q6VKKKGW8GWQNEYMDRMV01QMK8",
"created_at": "2021-03-31T18:27:07.803Z",
"updated_at": "2021-03-31T18:27:07Z",
"given_name": "Amelia",
"family_name": "Earhart",
"email_address": "Amelia.Earhart@example.com",
"preferences": {
"email_unsubscribed": false
}
}
}
*/
$card = new \Square\Models\Card();
$card->setCardholderName($this->square_driver->client->present()->name());
$card->setBillingAddress($billing_address);
$card->setCustomerId($result->getCustomer()->getId());
$card->setReferenceId(Str::random(8));
$body = new \Square\Models\CreateCardRequest(
Str::random(32),
$payment->payment->id,
$card
);
$api_response = $this->square_driver
->square
->getCardsApi()
->createCard($body);
if ($api_response->isSuccess()) {
$result = $api_response->getResult();
nlog($result->getBody());
nlog("ressy");
nlog($result);
} else {
$errors = $api_response->getErrors();
nlog("i got errors");
nlog($errors);
}
/**
*
{
"card": {
"id": "ccof:uIbfJXhXETSP197M3GB", //this is the token
"billing_address": {
"address_line_1": "500 Electric Ave",
"address_line_2": "Suite 600",
"locality": "New York",
"administrative_district_level_1": "NY",
"postal_code": "10003",
"country": "US"
},
"bin": "411111",
"card_brand": "VISA",
"card_type": "CREDIT",
"cardholder_name": "Amelia Earhart",
"customer_id": "Q6VKKKGW8GWQNEYMDRMV01QMK8",
"enabled": true,
"exp_month": 11,
"exp_year": 2018,
"last_4": "1111",
"prepaid_type": "NOT_PREPAID",
"reference_id": "user-id-1",
"version": 1
}
}
*/
$cgt = [];
$cgt['token'] = $result->getId();
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
$payment_meta = new \stdClass;
$payment_meta->exp_month = $result->getExpMonth();
$payment_meta->exp_year = $result->getExpYear();
$payment_meta->brand = $result->getCardBrand();
$payment_meta->last4 = $result->getLast4();
$payment_meta->type = GatewayType::CREDIT_CARD;
$cgt['payment_meta'] = $payment_meta;
$token = $this->square_driver->storeGatewayToken($cgt, []);
return back();
}
public function paymentView($data)
{
$data['gateway'] = $this->square_driver;
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
return render('gateways.braintree.credit_card.pay', $data);
}
public function processPaymentResponse($request)
{
}
/* This method is stubbed ready to go - you just need to harvest the equivalent 'transaction_reference' */
private function processSuccessfulPayment($response)
{
$amount = array_sum(array_column($this->square_driver->payment_hash->invoices(), 'amount')) + $this->square_driver->payment_hash->fee_total;
$payment_record = [];
$payment_record['amount'] = $amount;
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
// $payment_record['transaction_reference'] = $response->transaction_id;
$payment = $this->square_driver->createPayment($payment_record, Payment::STATUS_COMPLETED);
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
}
private function processUnsuccessfulPayment($response)
{
/*Harvest your own errors here*/
// $error = $response->status_message;
// if(property_exists($response, 'approval_message') && $response->approval_message)
// $error .= " - {$response->approval_message}";
// $error_code = property_exists($response, 'approval_message') ? $response->approval_message : 'Undefined code';
$data = [
'response' => $response,
'error' => $error,
'error_code' => $error_code,
];
return $this->square_driver->processUnsuccessfulTransaction($data);
}
/* Helpers */
/*
You will need some helpers to handle successful and unsuccessful responses
Some considerations after a succesful transaction include:
Logging of events: success +/- failure
Recording a payment
Notifications
*/
}

View File

@ -0,0 +1,105 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers;
use App\Http\Requests\Payments\PaymentWebhookRequest;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\SystemLog;
use App\PaymentDrivers\Square\CreditCard;
use App\Utils\Traits\MakesHash;
class SquarePaymentDriver extends BaseDriver
{
use MakesHash;
public $refundable = true; //does this gateway support refunds?
public $token_billing = true; //does this gateway support token billing?
public $can_authorise_credit_card = true; //does this gateway support authorizations?
public $square;
public $payment_method;
public static $methods = [
GatewayType::CREDIT_CARD => CreditCard::class, //maps GatewayType => Implementation class
];
const SYSTEM_LOG_TYPE = SystemLog::TYPE_SQUARE;
public function init()
{
$this->square = new \Square\SquareClient([
'accessToken' => $this->company_gateway->getConfigField('accessToken'),
'environment' => $this->company_gateway->getConfigField('testMode') ? \Square\Environment::SANDBOX : \Square\Environment::PRODUCTION,
]);
return $this; /* This is where you boot the gateway with your auth credentials*/
}
/* Returns an array of gateway types for the payment gateway */
public function gatewayTypes(): array
{
$types = [];
$types[] = GatewayType::CREDIT_CARD;
return $types;
}
/* Sets the payment method initialized */
public function setPaymentMethod($payment_method_id)
{
$class = self::$methods[$payment_method_id];
$this->payment_method = new $class($this);
return $this;
}
public function authorizeView(array $data)
{
return $this->payment_method->authorizeView($data); //this is your custom implementation from here
}
public function authorizeResponse($request)
{
return $this->payment_method->authorizeResponse($request); //this is your custom implementation from here
}
public function processPaymentView(array $data)
{
return $this->payment_method->paymentView($data); //this is your custom implementation from here
}
public function processPaymentResponse($request)
{
return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
}
public function refund(Payment $payment, $amount, $return_client_response = false)
{
//this is your custom implementation from here
}
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
{
//this is your custom implementation from here
}
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
{
}
}

View File

@ -69,6 +69,7 @@
"pragmarx/google2fa": "^8.0",
"predis/predis": "^1.1",
"sentry/sentry-laravel": "^2",
"square/square": "13.0.0.20210721",
"stripe/stripe-php": "^7.50",
"symfony/http-client": "^5.2",
"tijsverkoyen/css-to-inline-styles": "^2.2",

175
composer.lock generated
View File

@ -4,8 +4,122 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "275a9dd3910b6ec79607b098406dc6c7",
"content-hash": "93253273cd8399a0e083a064160b70bf",
"packages": [
{
"name": "apimatic/jsonmapper",
"version": "v2.0.3",
"source": {
"type": "git",
"url": "https://github.com/apimatic/jsonmapper.git",
"reference": "f7588f1ab692c402a9118e65cb9fd42b74e5e0db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/apimatic/jsonmapper/zipball/f7588f1ab692c402a9118e65cb9fd42b74e5e0db",
"reference": "f7588f1ab692c402a9118e65cb9fd42b74e5e0db",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
"squizlabs/php_codesniffer": "^3.0.0"
},
"type": "library",
"autoload": {
"psr-4": {
"apimatic\\jsonmapper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"OSL-3.0"
],
"authors": [
{
"name": "Christian Weiske",
"email": "christian.weiske@netresearch.de",
"homepage": "http://www.netresearch.de/",
"role": "Developer"
},
{
"name": "Mehdi Jaffery",
"email": "mehdi.jaffery@apimatic.io",
"homepage": "http://apimatic.io/",
"role": "Developer"
}
],
"description": "Map nested JSON structures onto PHP classes",
"support": {
"email": "mehdi.jaffery@apimatic.io",
"issues": "https://github.com/apimatic/jsonmapper/issues",
"source": "https://github.com/apimatic/jsonmapper/tree/v2.0.3"
},
"time": "2021-07-16T09:02:23+00:00"
},
{
"name": "apimatic/unirest-php",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/apimatic/unirest-php.git",
"reference": "b4e399a8970c3a5c611f734282f306381f9d1eee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/apimatic/unirest-php/zipball/b4e399a8970c3a5c611f734282f306381f9d1eee",
"reference": "b4e399a8970c3a5c611f734282f306381f9d1eee",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.6.0"
},
"require-dev": {
"phpunit/phpunit": "^5 || ^6 || ^7"
},
"suggest": {
"ext-json": "Allows using JSON Bodies for sending and parsing requests"
},
"type": "library",
"autoload": {
"psr-0": {
"Unirest\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mashape",
"email": "opensource@mashape.com",
"homepage": "https://www.mashape.com",
"role": "Developer"
},
{
"name": "APIMATIC",
"email": "opensource@apimatic.io",
"homepage": "https://www.apimatic.io",
"role": "Developer"
}
],
"description": "Unirest PHP",
"homepage": "https://github.com/apimatic/unirest-php",
"keywords": [
"client",
"curl",
"http",
"https",
"rest"
],
"support": {
"email": "opensource@apimatic.io",
"issues": "https://github.com/apimatic/unirest-php/issues",
"source": "https://github.com/apimatic/unirest-php/tree/2.0.0"
},
"time": "2020-04-07T17:16:29+00:00"
},
{
"name": "asm/php-ansible",
"version": "dev-main",
@ -7459,6 +7573,63 @@
],
"time": "2021-06-16T09:26:40+00:00"
},
{
"name": "square/square",
"version": "13.0.0.20210721",
"source": {
"type": "git",
"url": "https://github.com/square/square-php-sdk.git",
"reference": "03d90445854cd3b500f75061a9c63956799b8ecf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/square/square-php-sdk/zipball/03d90445854cd3b500f75061a9c63956799b8ecf",
"reference": "03d90445854cd3b500f75061a9c63956799b8ecf",
"shasum": ""
},
"require": {
"apimatic/jsonmapper": "^2.0.2",
"apimatic/unirest-php": "^2.0",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": ">=7.2"
},
"require-dev": {
"phan/phan": "^3.0",
"phpunit/phpunit": "^7.5 || ^8.5",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Square\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Square Developer Platform",
"email": "developers@squareup.com",
"homepage": "https://squareup.com/developers"
}
],
"description": "Use Square APIs to manage and run business including payment, customer, product, inventory, and employee management.",
"homepage": "https://squareup.com/developers",
"keywords": [
"api",
"sdk",
"square"
],
"support": {
"issues": "https://github.com/square/square-php-sdk/issues",
"source": "https://github.com/square/square-php-sdk/tree/13.0.0.20210721"
},
"time": "2021-07-21T06:43:15+00:00"
},
{
"name": "stripe/stripe-php",
"version": "v7.88.0",
@ -14972,5 +15143,5 @@
"platform-dev": {
"php": "^7.3|^7.4|^8.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.0.0"
}

View File

@ -90,6 +90,7 @@ return [
'decrypted' => env('PAYTRACE_KEYS', ''),
],
'mollie' => env('MOLLIE_KEYS', ''),
'square' => env('SQUARE_KEYS',''),
],
'contact' => [
'email' => env('MAIL_FROM_ADDRESS'),

View File

@ -0,0 +1,49 @@
<?php
use App\Models\Gateway;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Eloquent\Model;
class SquarePaymentDriver extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Model::unguard();
$fields = new \stdClass;
$fields->accessToken = "";
$fields->applicationId = "";
$fields->locationId = "";
$fields->testMode = false;
$square = new Gateway();
$square->id = 57;
$square->name = "Square";
$square->provider = "Square";
$square->key = '65faab2ab6e3223dbe848b1686490baz';
$square->sort_order = 4343;
$square->is_offsite = false;
$square->visible = true;
$square->fields = json_encode($fields);
$square->save();
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -80,6 +80,7 @@ class PaymentLibrariesSeeder extends Seeder
['id' => 53, 'name' => 'PagSeguro', 'provider' => 'PagSeguro', 'key' => 'ef498756b54db63c143af0ec433da803', 'fields' => '{"email":"","token":"","sandbox":false}'],
['id' => 54, 'name' => 'PAYMILL', 'provider' => 'Paymill', 'key' => 'ca52f618a39367a4c944098ebf977e1c', 'fields' => '{"apiKey":""}'],
['id' => 55, 'name' => 'Custom', 'provider' => 'Custom', 'is_offsite' => true, 'sort_order' => 21, 'key' => '54faab2ab6e3223dbe848b1686490baa', 'fields' => '{"name":"","text":""}'],
['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","testMode":"false"}'],
];
foreach ($gateways as $gateway) {
@ -96,7 +97,7 @@ class PaymentLibrariesSeeder extends Seeder
Gateway::query()->update(['visible' => 0]);
Gateway::whereIn('id', [1,7,15,20,39,46,55,50])->update(['visible' => 1]);
Gateway::whereIn('id', [1,7,15,20,39,46,55,50,57])->update(['visible' => 1]);
if (Ninja::isHosted()) {
Gateway::whereIn('id', [20])->update(['visible' => 0]);

View File

@ -0,0 +1,167 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title'
=> ctrans('texts.payment_type_credit_card')])
@section('gateway_head')
@endsection
@section('gateway_content')
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}"
method="post" id="server_response">
@csrf
<input type="text" name="sourceId" id="sourceId" hidden>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element-single')
<div id="card-container"></div>
<div id="payment-status-container"></div>
</form>
@endcomponent
@component('portal.ninja2020.gateways.includes.pay_now')
{{ ctrans('texts.add_payment_method') }}
@endcomponent
@endsection
@section('gateway_footer')
<script
type="text/javascript"
src="https://sandbox.web.squarecdn.com/v1/square.js"
></script>
<script>
const appId = "{{ $gateway->company_gateway->getConfigField('applicationId') }}";
const locationId = "{{ $gateway->company_gateway->getConfigField('locationId') }}";
const darkModeCardStyle = {
'.input-container': {
borderColor: '#2D2D2D',
borderRadius: '6px',
},
'.input-container.is-focus': {
borderColor: '#006AFF',
},
'.input-container.is-error': {
borderColor: '#ff1600',
},
'.message-text': {
color: '#999999',
},
'.message-icon': {
color: '#999999',
},
'.message-text.is-error': {
color: '#ff1600',
},
'.message-icon.is-error': {
color: '#ff1600',
},
input: {
backgroundColor: '#2D2D2D',
color: '#FFFFFF',
fontFamily: 'helvetica neue, sans-serif',
},
'input::placeholder': {
color: '#999999',
},
'input.is-error': {
color: '#ff1600',
},
};
async function initializeCard(payments) {
const card = await payments.card({
style: darkModeCardStyle,
});
await card.attach('#card-container');
return card;
}
async function tokenize(paymentMethod) {
const tokenResult = await paymentMethod.tokenize();
if (tokenResult.status === 'OK') {
return tokenResult.token;
} else {
let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
if (tokenResult.errors) {
errorMessage += ` and errors: ${JSON.stringify(
tokenResult.errors
)}`;
}
throw new Error(errorMessage);
}
}
// status is either SUCCESS or FAILURE;
function displayPaymentResults(status) {
const statusContainer = document.getElementById(
'payment-status-container'
);
if (status === 'SUCCESS') {
statusContainer.classList.remove('is-failure');
statusContainer.classList.add('is-success');
} else {
statusContainer.classList.remove('is-success');
statusContainer.classList.add('is-failure');
}
statusContainer.style.visibility = 'visible';
}
document.addEventListener('DOMContentLoaded', async function () {
if (!window.Square) {
throw new Error('Square.js failed to load properly');
}
let payments;
try {
payments = window.Square.payments(appId, locationId);
} catch {
const statusContainer = document.getElementById(
'payment-status-container'
);
statusContainer.className = 'missing-credentials';
statusContainer.style.visibility = 'visible';
return;
}
let card;
try {
card = await initializeCard(payments);
} catch (e) {
console.error('Initializing Card failed', e);
return;
}
async function handlePaymentMethodSubmission(event, paymentMethod) {
event.preventDefault();
try {
// disable the submit button as we await tokenization and make a payment request.
cardButton.disabled = true;
const token = await tokenize(paymentMethod);
document.getElementById('sourceId').value = token;
document.getElementById('server_response').submit();
displayPaymentResults('SUCCESS');
} catch (e) {
cardButton.disabled = false;
displayPaymentResults('FAILURE');
console.error(e.message);
}
}
const cardButton = document.getElementById('pay-now');
cardButton.addEventListener('click', async function (event) {
await handlePaymentMethodSubmission(event, card);
});
});
</script>
@endsection