ok
This commit is contained in:
147
package-lock.json
generated
147
package-lock.json
generated
@@ -12,9 +12,11 @@
|
|||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3190,6 +3192,12 @@
|
|||||||
"@sinonjs/commons": "^1.7.0"
|
"@sinonjs/commons": "^1.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||||
@@ -6893,6 +6901,66 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
|
||||||
|
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.17.1",
|
||||||
|
"xmlhttprequest-ssl": "~2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-client/node_modules/debug": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-client/node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.18.2",
|
"version": "5.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz",
|
||||||
@@ -13667,6 +13735,15 @@
|
|||||||
"teleport": ">=0.2.0"
|
"teleport": ">=0.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode.react": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.13.0",
|
"version": "6.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||||
@@ -15010,6 +15087,68 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||||
|
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.6.1",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-client/node_modules/debug": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser/node_modules/debug": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sockjs": {
|
"node_modules/sockjs": {
|
||||||
"version": "0.3.24",
|
"version": "0.3.24",
|
||||||
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
||||||
@@ -17481,6 +17620,14 @@
|
|||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
26
src/App.js
26
src/App.js
@@ -1,12 +1,34 @@
|
|||||||
import logo from './logo.svg';
|
// src/App.js
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import Checkout from './Checkout';
|
import Checkout from './Checkout';
|
||||||
|
import socket from './socket';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [socketId, setSocketId] = useState(null);
|
||||||
|
const [transactionSuccess, setTransactionSuccess] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.on('connect', () => {
|
||||||
|
console.log('Connected with socket ID:', socket.id);
|
||||||
|
setSocketId(socket.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('transactionSuccess', (data) => {
|
||||||
|
console.log('Transaction success:', data);
|
||||||
|
setTransactionSuccess(data); // data bisa berisi transactionId, status, dll
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off('connect');
|
||||||
|
socket.off('transactionSuccess');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<main>
|
<main>
|
||||||
<Checkout />
|
<Checkout socketId={socketId} transactionSuccess={transactionSuccess} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -265,3 +265,63 @@
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.checkmark-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
animation: fadeIn 0.5s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke: #4CAF50;
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
animation: scaleIn 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark-circle {
|
||||||
|
stroke-dasharray: 166;
|
||||||
|
stroke-dashoffset: 166;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke: #4CAF50;
|
||||||
|
fill: none;
|
||||||
|
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkmark-check {
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
stroke-dasharray: 48;
|
||||||
|
stroke-dashoffset: 48;
|
||||||
|
stroke: #4CAF50;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
animation: stroke 0.3s 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes stroke {
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scaleIn {
|
||||||
|
0% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
236
src/Checkout.js
236
src/Checkout.js
@@ -1,7 +1,123 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import './Checkout.css'; // Assuming you'll create a CSS file for styling
|
import './Checkout.css';
|
||||||
|
import { QRCodeCanvas } from 'qrcode.react';
|
||||||
|
|
||||||
|
const Checkout = ({ socketId, transactionSuccess }) => {
|
||||||
|
const [qrisData, setQrisData] = useState(null); // QRIS string
|
||||||
|
const [value, setValue] = useState(null); // QRIS value (optional)
|
||||||
|
const [products, setProducts] = useState([]); // Produk dari itemsId
|
||||||
|
const [loadingProducts, setLoadingProducts] = useState(false);
|
||||||
|
|
||||||
|
// Helper get cookie value
|
||||||
|
const getCookie = (name) => {
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; ${name}=`);
|
||||||
|
if (parts.length === 2) return parts.pop().split(';').shift();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
const fetchProducts = async () => {
|
||||||
|
const itemsIdRaw = getCookie('itemsId');
|
||||||
|
if (!itemsIdRaw) return;
|
||||||
|
|
||||||
|
let itemsId = [];
|
||||||
|
try {
|
||||||
|
itemsId = JSON.parse(itemsIdRaw);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Gagal parse itemsId dari cookie:', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsId.length === 0) return;
|
||||||
|
|
||||||
|
setLoadingProducts(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = getCookie('token');
|
||||||
|
if (!token) {
|
||||||
|
console.warn('Token tidak ditemukan');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
itemsId.forEach(id => params.append('itemsId', id));
|
||||||
|
|
||||||
|
const res = await fetch(`https://bot.kediritechnopark.com/webhook/store-dev/products`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: params.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
setProducts(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching products:', error);
|
||||||
|
} finally {
|
||||||
|
setLoadingProducts(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchProducts();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
const handlePay = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let itemsIdRaw = getCookie('itemsId');
|
||||||
|
let token = getCookie('token');
|
||||||
|
|
||||||
|
if (!itemsIdRaw || !token) {
|
||||||
|
alert("Token atau itemsId tidak ditemukan di cookies.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemsId = [];
|
||||||
|
try {
|
||||||
|
itemsId = JSON.parse(itemsIdRaw);
|
||||||
|
} catch (e) {
|
||||||
|
alert("Gagal parsing itemsId.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
itemsId.forEach(id => params.append('itemsId', id));
|
||||||
|
params.append('socketId', socketId);
|
||||||
|
|
||||||
|
|
||||||
|
const response = await fetch('https://bot.kediritechnopark.com/webhook/store-dev/pay', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: params.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result[0].qris_dynamic) {
|
||||||
|
setQrisData(result[0].qris_dynamic);
|
||||||
|
setValue(result[0].total_price);
|
||||||
|
} else {
|
||||||
|
alert(`Gagal mendapatkan QRIS: ${result?.error || 'Unknown error'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Network error:', error);
|
||||||
|
alert("Terjadi kesalahan jaringan.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Checkout = () => {
|
|
||||||
return (
|
return (
|
||||||
<div className="checkout-container">
|
<div className="checkout-container">
|
||||||
<div className="left-panel">
|
<div className="left-panel">
|
||||||
@@ -14,43 +130,83 @@ const Checkout = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="right-panel">
|
<div className="right-panel">
|
||||||
<button className="apple-pay-button"> Pay</button>
|
{!qrisData ? (
|
||||||
<div className="separator">
|
<>
|
||||||
<hr className="line" />
|
<h3>Cart Items</h3>
|
||||||
<span>Or pay another way</span>
|
{loadingProducts ? (
|
||||||
<hr className="line" />
|
<p>Loading products...</p>
|
||||||
</div>
|
) : products.length === 0 ? (
|
||||||
<form className="shipping-info">
|
<p>No products found</p>
|
||||||
<h3>Shipping information</h3>
|
) : (
|
||||||
<input type="email" placeholder="Email" required />
|
<ul>
|
||||||
<input type="text" placeholder="Name" required />
|
{products.map((product) => (
|
||||||
<select required>
|
<li key={product.id || product._id}>
|
||||||
<option value="United States">United States</option>
|
{product.name} - ${product.price}
|
||||||
</select>
|
</li>
|
||||||
<input type="text" placeholder="Address" required />
|
))}
|
||||||
<a href="#" className="manual-address">Enter address manually</a>
|
</ul>
|
||||||
</form>
|
)}
|
||||||
<div className="payment-methods">
|
|
||||||
<h3>Payment method</h3>
|
<form className="shipping-info" onSubmit={handlePay}>
|
||||||
<label>
|
<h3>Shipping information</h3>
|
||||||
<input type="radio" name="payment" value="card" />
|
<input type="email" placeholder="Email" />
|
||||||
<span className="icon">💳</span> Card
|
<input type="text" placeholder="Name" />
|
||||||
</label>
|
<select >
|
||||||
<label>
|
<option value="United States">United States</option>
|
||||||
<input type="radio" name="payment" value="bank" />
|
</select>
|
||||||
<span className="icon">🏦</span> Bank
|
<input type="text" placeholder="Address" />
|
||||||
</label>
|
<a href="#" className="manual-address">Enter address manually</a>
|
||||||
<label>
|
|
||||||
<input type="radio" name="payment" value="klarna" />
|
<div className="payment-methods">
|
||||||
<span className="icon klarna">K</span> Klarna
|
<h3>Payment method</h3>
|
||||||
<div className="klarna-subtext">Buy now pay later</div>
|
<label>
|
||||||
</label>
|
<input type="radio" name="payment" value="card" />
|
||||||
<label>
|
<span className="icon">💳</span> Card
|
||||||
<input type="radio" name="payment" value="ideal" />
|
</label>
|
||||||
<span className="icon ideal">iD</span> iDEAL
|
<label>
|
||||||
</label>
|
<input type="radio" name="payment" value="bank" />
|
||||||
</div>
|
<span className="icon">🏦</span> Bank
|
||||||
<button type="submit" className="pay-button">Pay</button>
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="payment" value="klarna" />
|
||||||
|
<span className="icon klarna">K</span> Klarna
|
||||||
|
<div className="klarna-subtext">Buy now pay later</div>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="payment" value="ideal" />
|
||||||
|
<span className="icon ideal">iD</span> iDEAL
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" className="pay-button">Pay</button>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{transactionSuccess ? (
|
||||||
|
<div className="success-section">
|
||||||
|
<div className="checkmark-container">
|
||||||
|
<svg className="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
|
||||||
|
<circle className="checkmark-circle" cx="26" cy="26" r="25" fill="none" />
|
||||||
|
<path className="checkmark-check" fill="none" d="M14 27l7 7 16-16" />
|
||||||
|
</svg>
|
||||||
|
<h2>Payment Successful!</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="qris-section">
|
||||||
|
<h3>Scan QRIS to Pay</h3>
|
||||||
|
<QRCodeCanvas value={qrisData} size={256} />
|
||||||
|
<p className="qris-string">{qrisData}</p>
|
||||||
|
</div>
|
||||||
|
<h1>{value}</h1>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
Powered by <strong>stripe</strong> | Terms | Privacy
|
Powered by <strong>stripe</strong> | Terms | Privacy
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
10
src/socket.js
Normal file
10
src/socket.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// src/socket.js
|
||||||
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
|
const socket = io('https://payment.kediritechnopark.com', {
|
||||||
|
transports: ['websocket'], // pastikan pakai websocket saja
|
||||||
|
secure: true,
|
||||||
|
reconnection: true
|
||||||
|
});
|
||||||
|
|
||||||
|
export default socket;
|
||||||
Reference in New Issue
Block a user