Make
checkout
great again
Make
checkout
great again
<input
autocomplete="street-address"
name="street-address"
type="text"
placeholder="Адрес" />
<input
autocomplete="address-level2"
name="address-level2"
type="text"
placeholder="Номер карты" />
<input
autocomplete="cc-number"
name="cc-number"
type="text"
placeholder="Город" />
if (window.PasswordCredential) {
//…
}
navigator.credentials.get({
password: true,
mediation: 'optional',
}).then({…});
navigator
.credentials
.preventSilentAccess();
navigator.credentials.get({…})
.then(credential => {
if (credential) {
// credential.password
// credential.id
}
});
<button id="pay">Купить</button >
const methodData = [{
supportedMethods: "basic-card",
}];
const methodData = [{
supportedMethods: "https://apple.com/apple-pay",
data: {
version: 3,
merchantIdentifier: "merchant.com.example",
merchantCapabilities: [
"supports3DS",
"supportsCredit",
"supportsDebit"],
supportedNetworks: ["amex", "discover", "masterCard", "visa"],
countryCode: "US",
},
}];
const details = {
id: "order-1",
displayItems: [{
label: "Вотлшебный Говорящий Медведь-Проститутка",
amount: {
currency: "USD",
value: "5.00"
},
},
{
label: "Cвинка",
amount: {
currency: "USD",
value: "3.00"
},
}],
total: {
label: "Всего",
amount: {
currency: "USD",
value: "8.00"
},
},
};
const options = {
requestPayerEmail: true,
requestPayerName: true,
requestPayerPhone: true,
requestShipping: false,
};
let request = new PaymentRequest(
methodData,
details,
options);
const button = document.getElementById('pay');
button.addEventListener('click', pay);
const pay = async function () {
const response = await request.show();
await response.complete(validate());
…
};
{
details: {},
methodName: "basic-card",
payerEmail: "thesilentimp@gmail.com",
payerName: "Anton",
payerPhone: "+380502773882",
requestId: "order-1",
shippingAddress: null,
shippingOption: null,
}
It's fine!
request.onmerchantvalidation =
async event => { … }
const response = await fetch('/validate', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: 'POST',
json: true,
body: JSON.stringify({ validationURL: event.validationURL }),
});
if (response.ok) {
return event.complete(response.json());
}
app.post(
'/validate',
async (req, res, next) => {…}
);
const certPath = resolve(__dirname, 'apple.cer');
const keyPath = resolve(__dirname, 'apple.cer');
const cert = fs.readFileSync(certPath);
const key = fs.readFileSync(keyPath);
const { validationURL } = req.body;
const appleURL =
url.parse(`${validationURL}/paymentSession`);
const options = {
key,
cert,
hostname: appleURL.hostname,
path: appleURL.path,
method: 'POST',
"headers": {
"Content-Type": "application/json",
},
json: true,
};
const POSTJSONString = JSON.stringify({
merchantIdentifier: 'merchant.moto.courses',
displayName: 'Moto Courses',
initiative: 'web',
initiativeContext: 'moto.courses',
});
const HTTPBody = Buffer.from(POSTJSONString, "utf8");
import https from 'https';
…
const request = https.request(
options,
function(responce) {…}
);
request.on('error', err => {…});
request.write(HTTPBody);
request.end();
responce.on('end', () => {
res.send(JSON.stringify(response));
});
$ openssl
req -sha256 -nodes -newkey rsa:2048
-keyout applepaytls.key
-out applepaytls.csr
$ openssl
x509 -inform der
-in certFromApple.cer
-out merchant_identity.pem
const certPath =
resolve(__dirname, 'merchant_identity.pem');
const keyPath =
resolve(__dirname, 'applepaytls.key');
https://qp.payhub.com.ua/#/payment/13976
?iframe=qp
&EMAIL=test@test.com
&FIO=Иванов
&PH=0501111111
&amount=1000
&autoCheck
<script src="https://js.stripe.com/v3/">
if (Stripe !== undefined) {
const stripe = Stripe('pk_cQQjittH');
// …
}
const items = [{
amount: 4000
, label: 'maintenance.course'
}];
const paymentRequest = stripe.paymentRequest({
country: 'US'
, currency: 'uah'
, requestPayerName: true
, requestPayerEmail: true
, requestPayerPhone: true
, requestShipping: false
, displayItems: items
, total: {
label: 'Мастеркласс'
, amount: 4000
}});
const elements = stripe.elements();
const prButton = elements.create(
'paymentRequestButton',
{ paymentRequest }
);
const response = await fetch('/skus');
const sku = await response.json();
const stripe = require('stripe')('sku_CckkusdfZ');
stripe.skus.retrieve('sku_JENMndnU3jS',
(error, sku) => {
if (error === null) {
res.send(JSON.stringify(sku)).status(200).end();
} else {
res.status(error.statusCode, error.message)
.end(http.STATUS_CODES[error.statusCode]);
}});
const result = await paymentRequest.canMakePayment();
if (result) {
prButton.mount('#payment-request-button');
} else {
// …
}
paymentRequest.on('token', async (event) => {
const {
token,
payerPhone:phone,
payerName:name,
payerEmail:email,
} = event;
const response = await fetch('/order', {
method: 'POST'
, body: JSON.stringify({ token, sku, phone, email, name })
, headers: {'content-type': 'application/json'}
});
});
const order = await stripe.orders.create({
email,
, currency: sku.currency
, items: [{
type: 'sku'
, parent: sku.id
}]});
const paidOrder = await stripe.orders.pay(order.id, {
source: (token.livemode ? token.id : 'tok_visa'),
});
const fulfilledOrder = await stripe.orders
.update(order.id, {
status: 'fulfilled'
});
res.send(JSON.stringify(fulfilledOrder))
.status(200).end();
Антон Немцев
http://silentimp.info/
@silentimp
thesilentimp@gmail.com
skype: ravencry