payment_request.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. date_default_timezone_set("Australia/Hobart");
  3. //error_reporting(E_ERROR | E_PARSE);
  4. error_reporting(E_ALL);
  5. ini_set('display_errors', '0');
  6. ini_set('log_errors', '1');
  7. require_once 'connection.php';
  8. include_once "vendor/autoload.php";
  9. $accessToken = 'pat-na1-64db9489-15fe-461d-b64c-941c4d80ba8a';
  10. $enquiry_date = date("l dS M \'y");
  11. $drg = isset($_GET['drg']) ? $_GET['drg'] : '';
  12. $payment_no = isset($_GET['pn']) ? $_GET['pn'] : '';
  13. if (!empty($_GET['drg'])) {
  14. include "table.php";
  15. }
  16. ?>
  17. <!doctype html>
  18. <html lang="en">
  19. <head>
  20. <meta charset="utf-8">
  21. <meta name="viewport" content="width=device-width, initial-scale=1">
  22. <title>Payment Request Form</title>
  23. <link rel="shortcut icon" href="images/blueprint.ico" type="image/x-icon">
  24. <link href="css/bootstrap.css" rel="stylesheet">
  25. <link href="css/blueprint.css" rel="stylesheet">
  26. <link href="css/print.css" rel="stylesheet" media="print">
  27. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
  28. <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
  29. <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
  30. <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
  31. <script type="text/javascript" src="https://use.fontawesome.com/1e2844bb90.js"></script>
  32. <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" integrity="sha256-T0Vest3yCU7pafRw9r+settMBX6JkKN06dqBnpQ8d30=" crossorigin="anonymous"></script>
  33. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-throttle-debounce/1.1/jquery.ba-throttle-debounce.min.js" integrity="sha512-JZSo0h5TONFYmyLMqp8k4oPhuo6yNk9mHM+FY50aBjpypfofqtEWsAgRDQm94ImLCzSaHeqNvYuD9382CEn2zw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  34. </head>
  35. <body>
  36. <nav class="navbar bg-brown-dark brown-light border-bottom border-body d-print-none" data-bs-theme="dark">
  37. <div class="container-fluid">
  38. <a class="navbar-brand brown-light" href="#">
  39. <img src="images/blueprint-logo-light.png" alt="Logo" width="30" height="24" class="d-inline-block align-text-top">
  40. Modulos Design
  41. </a>
  42. </div>
  43. </nav>
  44. <div class="container">
  45. <div class="row pt-2">
  46. <div class="col-sm-4 col-md-4 pt-3">
  47. <img class="img-fluid logo pt-2" src="images/blueprint-full-logo-medium.png" alt="Blueprint Studio">
  48. </div>
  49. <div class="col-sm-4 col-md-4 m-auto text-center">
  50. <h3 class="architect text-center">Job: <?php echo $drg; ?></h3>
  51. </div>
  52. <div class="col-sm-4 col-md-4 text-end pt-3">
  53. <h2 class="fw-bold text-end mb-1">Client Onboarding Form</h2>
  54. <h4 class="text-end mb-1">
  55. <span class="fw-bold brown-two"><?php echo $enquiry_date; ?></span>
  56. </h4>
  57. </div>
  58. </div>
  59. <script type="text/javascript" src="https://sandbox.web.squarecdn.com/v1/square.js" ></script>
  60. <script>
  61. const appId = '{sandbox-sq0idb-ZUSNynWbeVYrFo3hjZFIQg}';
  62. const locationId = '{LYQ568H0H05Q2}';
  63. async function initializeCard(payments) {
  64. const card = await payments.card();
  65. await card.attach('#card-container');
  66. return card;
  67. }
  68. async function createPayment(token, verificationToken) {
  69. const body = JSON.stringify({
  70. locationId,
  71. sourceId: token,
  72. verificationToken,
  73. idempotencyKey: window.crypto.randomUUID(),
  74. });
  75. const paymentResponse = await fetch('/payment', {
  76. method: 'POST',
  77. headers: {
  78. 'Content-Type': 'application/json',
  79. },
  80. body,
  81. });
  82. if (paymentResponse.ok) {
  83. return paymentResponse.json();
  84. }
  85. const errorBody = await paymentResponse.text();
  86. throw new Error(errorBody);
  87. }
  88. async function tokenize(paymentMethod) {
  89. const tokenResult = await paymentMethod.tokenize();
  90. if (tokenResult.status === 'OK') {
  91. return tokenResult.token;
  92. } else {
  93. let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
  94. if (tokenResult.errors) {
  95. errorMessage += ` and errors: ${JSON.stringify(
  96. tokenResult.errors,
  97. )}`;
  98. }
  99. throw new Error(errorMessage);
  100. }
  101. }
  102. // Required in SCA Mandated Regions: Learn more at https://developer.squareup.com/docs/sca-overview
  103. async function verifyBuyer(payments, token) {
  104. const verificationDetails = {
  105. amount: '100.00',
  106. billingContact: {
  107. givenName: '<?php echo $firstname; ?>',
  108. familyName: '<?php echo $lastname; ?>',
  109. email: '<?php echo $client_email; ?>',
  110. phone: '<?php echo $client_mobile; ?>',
  111. addressLines: ['<?php echo $postal_address_street; ?>'],
  112. city: '<?php echo $postal_address_town; ?>',
  113. state: '<?php echo $postal_address_state; ?>',
  114. countryCode: 'AU',
  115. },
  116. currencyCode: 'AUD',
  117. intent: 'CHARGE',
  118. };
  119. const verificationResults = await payments.verifyBuyer(
  120. token,
  121. verificationDetails,
  122. );
  123. return verificationResults.token;
  124. }
  125. // status is either SUCCESS or FAILURE;
  126. function displayPaymentResults(status) {
  127. const statusContainer = document.getElementById(
  128. 'payment-status-container',
  129. );
  130. if (status === 'SUCCESS') {
  131. statusContainer.classList.remove('is-failure');
  132. statusContainer.classList.add('is-success');
  133. } else {
  134. statusContainer.classList.remove('is-success');
  135. statusContainer.classList.add('is-failure');
  136. }
  137. statusContainer.style.visibility = 'visible';
  138. }
  139. document.addEventListener('DOMContentLoaded', async function () {
  140. if (!window.Square) {
  141. throw new Error('Square.js failed to load properly');
  142. }
  143. let payments;
  144. try {
  145. payments = window.Square.payments(appId, locationId);
  146. } catch {
  147. const statusContainer = document.getElementById(
  148. 'payment-status-container',
  149. );
  150. statusContainer.className = 'missing-credentials';
  151. statusContainer.style.visibility = 'visible';
  152. return;
  153. }
  154. let card;
  155. try {
  156. card = await initializeCard(payments);
  157. } catch (e) {
  158. console.error('Initializing Card failed', e);
  159. return;
  160. }
  161. async function handlePaymentMethodSubmission(event, card) {
  162. event.preventDefault();
  163. try {
  164. // disable the submit button as we await tokenization and make a payment request.
  165. cardButton.disabled = true;
  166. const token = await tokenize(card);
  167. const verificationToken = await verifyBuyer(payments, token);
  168. const paymentResults = await createPayment(
  169. token,
  170. verificationToken,
  171. );
  172. displayPaymentResults('SUCCESS');
  173. console.debug('Payment Success', paymentResults);
  174. } catch (e) {
  175. cardButton.disabled = false;
  176. displayPaymentResults('FAILURE');
  177. console.error(e.message);
  178. }
  179. }
  180. const cardButton = document.getElementById('card-button');
  181. cardButton.addEventListener('click', async function (event) {
  182. await handlePaymentMethodSubmission(event, card);
  183. });
  184. });
  185. </script>
  186. <div class="row">
  187. <div class="col-12">
  188. <div class="row ">
  189. <div class="col ">
  190. <h4 class="fw-bold">Client Details</h4>
  191. </div>
  192. </div>
  193. <div class="mb-1">
  194. <div class="row ">
  195. <div class="col-6 col-md-3">
  196. <label for="firstname" class="form-label form-label-sm p-0 m-0">Clients Name</label>
  197. <input type="text" class="form-control form-control-sm architect brown-four" name="firstname" id="firstname" tabindex="1" value="<?php echo $firstname; ?>" readonly>
  198. </div>
  199. <div class="col-6 col-md-3">
  200. <label for="lastname" class="form-label form-label-sm p-0 m-0"></label>
  201. <input type="text" class="form-control form-control-sm architect brown-four" name="lastname" id="lastname" tabindex="2" value="<?php echo $lastname; ?>" readonly>
  202. </div>
  203. <div class="col-12 col-md-6">
  204. <label for="joint_name" class="form-label form-label-sm p-0 m-0">T/As - Joint Names</label>
  205. <input type="text" class="form-control form-control-sm architect brown-three" name="joint_name" id="joint_name" value="<?php echo $joint_name; ?>" readonly>
  206. </div>
  207. </div>
  208. </div>
  209. <div class="mb-1">
  210. <label for="postal_address" class="form-label form-label-sm p-0 m-0">Clients Postal Address</label>
  211. <input type="text" class="form-control form-control-sm fw-bold architect brown-three map-autocomplete" id="postal_address" name="postal_address" value="<?php echo $postal_address; ?>" readonly>
  212. </div>
  213. <div class="mb-1">
  214. <div class="row ">
  215. <div class="col-md-6">
  216. <label for="phoneNumber" class="form-label form-label-sm p-0 m-0">Clients Mobile</label>
  217. <input type="phone" class="form-control form-control-sm architect brown-three" minlength="12" id="phoneNumber" name="client_mobile" value="<?php echo $client_mobile; ?>" readonly>
  218. </div>
  219. <div class="col-md-6">
  220. <label for="client_email" class="form-label form-label-sm p-0 m-0">Email address</label>
  221. <input type="email" class="form-control form-control-sm architect brown-three" name="client_email" id="client_email" value="<?php echo $client_email; ?>" readonly>
  222. </div>
  223. </div>
  224. </div>
  225. </div>
  226. </div>
  227. <hr>
  228. <div class="row mt-3">
  229. <div class="col-md-1">Item</div>
  230. <div class="col-md-5">Description</div>
  231. <div class="col-md-2">Date Paid</div>
  232. <div class="col">Total</div>
  233. </div>
  234. <hr>
  235. <?php
  236. $checkRecord = mysqli_query($con, "SELECT * FROM `progress_payments` WHERE `drg` = '{$drg}' " );
  237. $rowcount = mysqli_num_rows($checkRecord);
  238. $result = mysqli_query($con, "SELECT *, @curRow := @curRow + 1 AS position FROM `progress_payments` JOIN (SELECT @curRow := 0) r WHERE `drg` = '{$drg}' ");
  239. if (!$result) {
  240. printf("Error: %s\n", mysqli_error($con));
  241. exit();
  242. }
  243. while ($row = mysqli_fetch_array($result)) {
  244. echo "<div class='mb-1 row justify-content-start'>";
  245. echo "<label for='description' class='col-1 col-form-label'>" . $row['position'] . "</label>";
  246. echo "<input type='hidden' id='progress' v-model='progress' value='" . $row['position'] . "'>";
  247. echo "<div class='col-sm-5'>";
  248. echo "<input type='text' class='form-control form-control-sm' id='description' v-model='description' value='" . $row['description'] . "' disabled>";
  249. echo "</div>";
  250. echo "<div class='col-sm-2'>";
  251. echo "<input type='text' class='form-control form-control-sm' id='paid' v-model='paid' value='" . $row['paid'] . "' disabled>";
  252. echo "</div>";
  253. echo "<div class='col-sm-2'>";
  254. echo "<input type='currency' class='form-control form-control-sm' id='value' v-model='value' value='" . $row['value'] . "' disabled>";
  255. echo "</div>";
  256. echo "<div class='col-sm-2'>";
  257. echo "<button class='btn btn-sm bg-brown-three brown-five' id='card-button' type='button' data-value=" . $row['value'] . " >Pay $" . $row['value'] . "</button>";
  258. echo "</div>";
  259. echo "</div>";
  260. }
  261. ?>
  262. <hr>
  263. <div class="row">
  264. <form id="payment-form">
  265. <div id="card-container"></div>
  266. <button class="btn btn-sm bg-brown-three brown-five" id="card-button" type="button">Pay $100.00</button>
  267. </form>
  268. <div id="payment-status-container"></div>
  269. </div>
  270. <script type="text/javascript" src="https://sandbox.web.squarecdn.com/v1/square.js" ></script>
  271. <script>
  272. const appId = '{sandbox-sq0idb-ZUSNynWbeVYrFo3hjZFIQg}';
  273. const locationId = '{LYQ568H0H05Q2}';
  274. async function initializeCard(payments) {
  275. const card = await payments.card();
  276. await card.attach('#card-container');
  277. return card;
  278. }
  279. async function createPayment(token, verificationToken) {
  280. const body = JSON.stringify({
  281. locationId,
  282. sourceId: token,
  283. verificationToken,
  284. idempotencyKey: window.crypto.randomUUID(),
  285. });
  286. const paymentResponse = await fetch('/payment', {
  287. method: 'POST',
  288. headers: {
  289. 'Content-Type': 'application/json',
  290. },
  291. body,
  292. });
  293. if (paymentResponse.ok) {
  294. return paymentResponse.json();
  295. }
  296. const errorBody = await paymentResponse.text();
  297. throw new Error(errorBody);
  298. }
  299. async function tokenize(paymentMethod) {
  300. const tokenResult = await paymentMethod.tokenize();
  301. if (tokenResult.status === 'OK') {
  302. return tokenResult.token;
  303. } else {
  304. let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
  305. if (tokenResult.errors) {
  306. errorMessage += ` and errors: ${JSON.stringify(
  307. tokenResult.errors,
  308. )}`;
  309. }
  310. throw new Error(errorMessage);
  311. }
  312. }
  313. const amount = event.currentTarget.getAttribute('data-value');
  314. // Required in SCA Mandated Regions: Learn more at https://developer.squareup.com/docs/sca-overview
  315. async function verifyBuyer(payments, token) {
  316. const verificationDetails = {
  317. amount: amount,
  318. billingContact: {
  319. givenName: '<?php echo $firstname; ?>',
  320. familyName: '<?php echo $lastname; ?>',
  321. email: '<?php echo $client_email; ?>',
  322. phone: '<?php echo $client_mobile; ?>',
  323. addressLines: ['<?php echo $postal_address_street; ?>'],
  324. city: '<?php echo $postal_address_town; ?>',
  325. state: '<?php echo $postal_address_state; ?>',
  326. countryCode: 'AU',
  327. },
  328. currencyCode: 'AUD',
  329. intent: 'CHARGE',
  330. };
  331. const verificationResults = await payments.verifyBuyer(
  332. token,
  333. verificationDetails,
  334. );
  335. return verificationResults.token;
  336. }
  337. // status is either SUCCESS or FAILURE;
  338. function displayPaymentResults(status) {
  339. const statusContainer = document.getElementById(
  340. 'payment-status-container',
  341. );
  342. if (status === 'SUCCESS') {
  343. statusContainer.classList.remove('is-failure');
  344. statusContainer.classList.add('is-success');
  345. //
  346. // Add date payment success to data base
  347. //
  348. } else {
  349. statusContainer.classList.remove('is-success');
  350. statusContainer.classList.add('is-failure');
  351. }
  352. statusContainer.style.visibility = 'visible';
  353. }
  354. document.addEventListener('DOMContentLoaded', async function () {
  355. if (!window.Square) {
  356. throw new Error('Square.js failed to load properly');
  357. }
  358. let payments;
  359. try {
  360. payments = window.Square.payments(appId, locationId);
  361. } catch {
  362. const statusContainer = document.getElementById(
  363. 'payment-status-container',
  364. );
  365. statusContainer.className = 'missing-credentials';
  366. statusContainer.style.visibility = 'visible';
  367. return;
  368. }
  369. let card;
  370. try {
  371. card = await initializeCard(payments);
  372. } catch (e) {
  373. console.error('Initializing Card failed', e);
  374. return;
  375. }
  376. async function handlePaymentMethodSubmission(event, card) {
  377. event.preventDefault();
  378. try {
  379. // disable the submit button as we await tokenization and make a payment request.
  380. cardButton.disabled = true;
  381. const token = await tokenize(card);
  382. const verificationToken = await verifyBuyer(payments, token);
  383. const paymentResults = await createPayment(
  384. token,
  385. verificationToken,
  386. );
  387. displayPaymentResults('SUCCESS');
  388. console.debug('Payment Success', paymentResults);
  389. } catch (e) {
  390. cardButton.disabled = false;
  391. displayPaymentResults('FAILURE');
  392. console.error(e.message);
  393. }
  394. }
  395. const cardButton = document.getElementById('card-button');
  396. cardButton.addEventListener('click', async function (event) {
  397. await handlePaymentMethodSubmission(event, card);
  398. });
  399. });
  400. </script>
  401. <footer class="footer">
  402. <p class="text-center">&copy; <?php echo date('Y'); ?> - Modulos Design</p>
  403. </footer>
  404. </div>
  405. <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
  406. <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB-QceOYrDe9otynMmQ9iNF3yEZzbpsanM&libraries=places&callback=initAutocomplete" async defer></script>
  407. <script src="https://cdn.jsdelivr.net/npm/signature_pad@4.0.0/dist/signature_pad.umd.min.js"></script>
  408. <script src="js/signature.js" ></script>
  409. <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
  410. <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
  411. </body>
  412. </html>