mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Square payments authorize
This commit is contained in:
parent
5c8976a50f
commit
19e9aac12b
@ -31,17 +31,17 @@ class CreditCard
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $square_class;
|
||||
public $square_driver;
|
||||
|
||||
public function __construct(SquarePaymentDriver $square_class)
|
||||
public function __construct(SquarePaymentDriver $square_driver)
|
||||
{
|
||||
$this->square_class = $square_class;
|
||||
$this->square_driver = $square_driver;
|
||||
}
|
||||
|
||||
public function authorizeView($data)
|
||||
{
|
||||
|
||||
$data['gateway'] = $this->square_class;
|
||||
$data['gateway'] = $this->square_driver;
|
||||
|
||||
return render('gateways.square.credit_card.authorize', $data);
|
||||
|
||||
@ -49,24 +49,136 @@ class CreditCard
|
||||
|
||||
public function authorizeRequest($request)
|
||||
{
|
||||
$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 = $client->getPaymentsApi()->createPayment($body);
|
||||
|
||||
if ($api_response->isSuccess()) {
|
||||
$result = $api_response->getResult();
|
||||
} else {
|
||||
$errors = $api_response->getErrors();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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_class->client->address1);
|
||||
$billing_address->setAddressLine2($this->square_class->client->address2);
|
||||
$billing_address->setLocality($this->square_class->client->city);
|
||||
$billing_address->setAdministrativeDistrictLevel1($this->square_class->client->state);
|
||||
$billing_address->setPostalCode($this->square_class->client->postal_code);
|
||||
$billing_address->setCountry($this->square_class->client->country->iso_3166_2);
|
||||
$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($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();
|
||||
} else {
|
||||
$errors = $api_response->getErrors();
|
||||
}
|
||||
|
||||
/*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('Amelia Earhart');
|
||||
$card->setBillingAddress($billing_address);
|
||||
$card->setCustomerId('VDKXEEKPJN48QDG3BGGFAK05P8');
|
||||
$card->setReferenceId('user-id-1');
|
||||
$card->setCardholderName($this->square_driver->client->present()->name());
|
||||
$card->setBillingAddress($address);
|
||||
$card->setCustomerId($result->customer->id);
|
||||
$card->setReferenceId(Str::random(8));
|
||||
|
||||
$body = new \Square\Models\CreateCardRequest(
|
||||
'4935a656-a929-4792-b97c-8848be85c27c',
|
||||
'cnon:uIbfJXhXETSP197M3GB',
|
||||
Str::random(32),
|
||||
$request->sourceId,
|
||||
$card
|
||||
);
|
||||
|
||||
@ -78,6 +190,53 @@ class CreditCard
|
||||
$errors = $api_response->getErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
{
|
||||
"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->card->id;
|
||||
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = $result->card->exp_month;
|
||||
$payment_meta->exp_year = $result->card->exp_year;
|
||||
$payment_meta->brand = $result->card->card_brand;
|
||||
$payment_meta->last4 = $result->card->last_4;
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
|
||||
$cgt['payment_meta'] = $payment_meta;
|
||||
|
||||
$token = $this->square_driver->storeGatewayToken($cgt, []);
|
||||
|
||||
|
||||
return back();
|
||||
}
|
||||
@ -85,7 +244,7 @@ class CreditCard
|
||||
public function paymentView($data)
|
||||
{
|
||||
|
||||
$data['gateway'] = $this->square_class;
|
||||
$data['gateway'] = $this->square_driver;
|
||||
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
|
||||
|
||||
return render('gateways.braintree.credit_card.pay', $data);
|
||||
@ -100,7 +259,7 @@ class CreditCard
|
||||
/* 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_class->payment_hash->invoices(), 'amount')) + $this->square_class->payment_hash->fee_total;
|
||||
$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;
|
||||
@ -108,7 +267,7 @@ class CreditCard
|
||||
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||
// $payment_record['transaction_reference'] = $response->transaction_id;
|
||||
|
||||
$payment = $this->square_class->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||
$payment = $this->square_driver->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
|
||||
@ -130,7 +289,7 @@ class CreditCard
|
||||
'error_code' => $error_code,
|
||||
];
|
||||
|
||||
return $this->square_class->processUnsuccessfulTransaction($data);
|
||||
return $this->square_driver->processUnsuccessfulTransaction($data);
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,45 +8,138 @@
|
||||
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}"
|
||||
method="post" id="server_response">
|
||||
@csrf
|
||||
<input type="txt" id=HPF_Token name= HPF_Token hidden>
|
||||
<input type="txt" id=enc_key name= enc_key hidden>
|
||||
<input type="text" name="token" hidden>
|
||||
<input type="text" name="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>
|
||||
<button id="card-button" type="button">Add</button>
|
||||
|
||||
<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
|
||||
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();
|
||||
const card = await payments.card({
|
||||
style: darkModeCardStyle,
|
||||
});
|
||||
await card.attach('#card-container');
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function () {
|
||||
async function createPayment(token) {
|
||||
|
||||
document.getElementById('sourceId').value = token;
|
||||
document.getElementById('server_response').submit();
|
||||
|
||||
const errorBody = await paymentResponse.text();
|
||||
throw new Error(errorBody);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
const payments = window.Square.payments(appId, locationId);
|
||||
let card;
|
||||
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) {
|
||||
@ -54,7 +147,28 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 5.2: create card payment
|
||||
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);
|
||||
const paymentResults = await createPayment(token);
|
||||
displayPaymentResults('SUCCESS');
|
||||
|
||||
console.debug('Payment Success', paymentResults);
|
||||
} 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>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user