我正在尝试使用 Node.js 和 Express 集成 Swish API 以进行支付请求。尽管密切关注文档,我仍然遇到 401 未经授权的错误:
{
"errorCode": "PA01",
"errorMessage": "Parameter is not correct.",
"additionalInformation": null
}
这是我当前的代码:
const express = require('express');
const fs = require('fs');
const crypto = require('crypto');
const https = require('https');
const path = require('path');
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
const forge = require('node-forge');
const { execSync } = require('child_process');
const app = express();
app.use(express.json());
const basePath = '/Users/xx/downloads/mss_test_2.0/Getswish_Test_Certificates/';
const authCertPath = path.join(basePath, 'Swish_Merchant_TestCertificate_1234679304.p12');
const signingCertPath = path.join(basePath, 'Swish_Merchant_TestSigningCertificate_1234679304.p12');
const caCertPath = path.join(basePath, 'Swish_TLS_RootCA.pem');
const passphrase = 'swish'; // Assuming this is the passphrase for both certificates
const getCertAndKeyFromP12 = (p12Path, passphrase) => {
const p12Content = fs.readFileSync(p12Path, 'binary');
const p12Asn1 = forge.asn1.fromDer(p12Content);
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, passphrase);
const keyObj = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag })[forge.pki.oids.pkcs8ShroudedKeyBag][0];
const certObj = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag][0];
const key = forge.pki.privateKeyToPem(keyObj.key);
const cert = forge.pki.certificateToPem(certObj.cert);
return { cert, key };
};
const getCertificateSerialNumber = (certPath, passphrase) => {
const certContent = fs.readFileSync(certPath, 'binary');
const p12Asn1 = forge.asn1.fromDer(certContent);
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, passphrase);
const certObj = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag][0];
const cert = forge.pki.certificateToPem(certObj.cert);
const serialNumber = certObj.cert.serialNumber.toUpperCase();
console.log(`Extracted Serial Number: ${serialNumber}`);
return serialNumber;
};
const getHashedPayload = (payload) => {
return crypto.createHash('sha512').update(payload).digest();
};
const getCertificateSignature = (hash, privateKey) => {
const sign = crypto.createSign('RSA-SHA512');
sign.update(hash);
sign.end();
return sign.sign(privateKey, 'base64');
};
app.post('/create-payout', (req, res) => {
const { cert, key: signingPrivateKey } = getCertAndKeyFromP12(signingCertPath, passphrase);
const payload = {
amount: parseFloat(req.body.amount,2).toString(),
currency: 'SEK',
instructionDate: new Date().toISOString(),
message: req.body.message,
payeeAlias: '0728648607',
payeeSSN: '196702234292',
payerAlias: '1234679304',
payerPaymentReference: 'mockedPayerPaymentReference',
payoutInstructionUUID: '822735C159A944DBB2B3546717BC919F',
payoutType: 'PAYOUT',
signingCertificateSerialNumber: getCertificateSerialNumber(signingCertPath, passphrase)
};
const payloadString = JSON.stringify(payload);
const payloadHash = getHashedPayload(payloadString);
const signature = getCertificateSignature(payloadHash, signingPrivateKey);
const requestBody = {
payload,
signature,
callbackUrl: 'https://xxxx.ngrok-free.app/swishcallback'
};
console.log(JSON.stringify(requestBody))
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody),
agent: new https.Agent({
pfx: fs.readFileSync(authCertPath),
passphrase: passphrase,
ca: fs.readFileSync(caCertPath)
})
};
fetch('https://mss.cpc.getswish.net/swish-cpcapi/api/v1/payouts', requestOptions)
.then(response => response.json())
.then(data => res.json(data))
.catch(error => res.status(500).json({ error: error.message }));
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
请求正文:
{
"amount": 100.00,
"message": "example message"
}
我按照 Swish API 文档使用 Node.js 和 Express 创建付款请求。我确保有效负载的格式正确,并使用所需的加密方法生成签名。我在请求设置中包含了必要的证书和密钥。
我正在寻找可能导致此错误的原因以及解决方法。任何帮助将不胜感激!
Swish API 文档: https://developer.swish.nu/documentation/environments https://developer.swish.nu/api/mss/v1#create-payout-request
针对 pA01 问题进行格式化
const express = require('express');
const fs = require('fs');
const crypto = require('crypto');
const https = require('https');
const path = require('path');
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
const forge = require('node-forge');
const app = express();
app.use(express.json());
const basePath = '/Users/xx/downloads/mss_test_2.0/Getswish_Test_Certificates/';
const authCertPath = path.join(basePath, 'Swish_Merchant_TestCertificate_1234679304.p12');
const signingCertPath = path.join(basePath, 'Swish_Merchant_TestSigningCertificate_1234679304.p12');
const caCertPath = path.join(basePath, 'Swish_TLS_RootCA.pem');
const passphrase = 'swish'; // Assuming this is the passphrase for both certificates
const getCertAndKeyFromP12 = (p12Path, passphrase) => {
const p12Content = fs.readFileSync(p12Path, 'binary');
const p12Asn1 = forge.asn1.fromDer(p12Content);
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, passphrase);
const keyObj = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag })[forge.pki.oids.pkcs8ShroudedKeyBag][0];
const certObj = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag][0];
const key = forge.pki.privateKeyToPem(keyObj.key);
const cert = forge.pki.certificateToPem(certObj.cert);
return { cert, key };
};
const getCertificateSerialNumber = (certPath, passphrase) => {
const certContent = fs.readFileSync(certPath, 'binary');
const p12Asn1 = forge.asn1.fromDer(certContent);
const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, passphrase);
const certObj = p12.getBags({ bagType: forge.pki.oids.certBag })[forge.pki.oids.certBag][0];
const serialNumber = certObj.cert.serialNumber.toUpperCase();
console.log(`Extracted Serial Number: ${serialNumber}`);
return serialNumber;
};
const getHashedPayload = (payload) => {
return crypto.createHash('sha512').update(payload).digest();
};
const getCertificateSignature = (hash, privateKey) => {
const sign = crypto.createSign('RSA-SHA512');
sign.update(hash);
sign.end();
return sign.sign(privateKey, 'base64');
};
app.post('/create-payout', (req, res) => {
const { cert, key: signingPrivateKey } = getCertAndKeyFromP12(signingCertPath, passphrase);
// Create the payload
const payload = {
amount: parseFloat(req.body.amount).toFixed(2), // Ensure amount is formatted correctly
currency: 'SEK',
instructionDate: new Date().toISOString().replace(/\.\d+Z$/, 'Z'), // Ensure correct format
payeeAlias: '0728648607',
payeeSSN: '196702234292',
payerAlias: '1234679304',
payerPaymentReference: 'mockedPayerPaymentReference',
payoutInstructionUUID: '822735C159A944DBB2B3546717BC919F',
payoutType: 'PAYOUT',
signingCertificateSerialNumber: getCertificateSerialNumber(signingCertPath, passphrase)
};
// Add message field if provided and valid
if (req.body.message && req.body.message.length <= 50) {
payload.message = req.body.message;
}
const payloadString = JSON.stringify(payload);
const payloadHash = getHashedPayload(payloadString);
const signature = getCertificateSignature(payloadHash, signingPrivateKey);
const requestBody = {
payload,
signature,
callbackUrl: 'https://xxxx.ngrok-free.app/swishcallback'
};
console.log('Request Body:', JSON.stringify(requestBody));
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody),
agent: new https.Agent({
pfx: fs.readFileSync(authCertPath),
passphrase: passphrase,
ca: fs.readFileSync(caCertPath)
})
};
fetch('https://mss.cpc.getswish.net/swish-cpcapi/api/v1/payouts', requestOptions)
.then(response => {
if (!response.ok) {
// Handle non-2xx responses
console.log('Error Response:', response.statusText);
return response.json().then(errorData => {
throw new Error(`Swish API error: ${errorData.errorCode} - ${errorData.errorMessage}`);
});
}
return response.json();
})
.then(data => {
console.log('API Response:', data);
res.json(data);
})
.catch(error => {
console.error('Request Error:', error.message);
res.status(500).json({ error: error.message });
});
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});