使用Stripe国际贸易收款(PHP第一版教程)
写在前面:
如果你的网站在线业务面向海外的话避免不了用户支付的问题,Stripe目前应该是做的比较好的一家支付平台了,除了信用卡外还支持国内的微信支付宝,但是手续费有点高。
回归正题,说说stripe的开发文档,乍一看stripe官方文档可能会一头雾水,没关系,本文以PHP+MySQL为例实现了支付和订单更新的实现,简单易懂,实际根据逻辑自行修改整合到自己的项目中即可。
在进入正文前需要注意准备的事项:
1.先到Stripe官网后台获取到apikey,这里不做过多阐述。

2.下载好官方的stripe-php核心库,你可以使用composer或者require_once都可以。
地址:https://github.com/stripe/stripe-php
进入主题
项目涉及的文件
/ ├── config.php ├── dbConnect.php ├── index.php ├── payment.php ├── stripe-php/ ├── css/style.css
用于测试支付的一些卡号:
4242424242424242–Visa4000056655665556–Visa (debit)5555555555554444–Mastercard5200828282828210–Mastercard (debit)378282246310005–American Express6011111111111117–Discover
1.准备数据表
CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `email` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `item_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `item_number` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `item_price` float(10,2) NOT NULL, `item_price_currency` varchar(10) COLLATE utf8_unicode_ci NOT NULL, `paid_amount` varchar(10) COLLATE utf8_unicode_ci NOT NULL, `paid_amount_currency` varchar(10) COLLATE utf8_unicode_ci NOT NULL, `txn_id` varchar(50) COLLATE utf8_unicode_ci NOT NULL, `payment_status` varchar(25) COLLATE utf8_unicode_ci NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
2.配置文件config.php
<?php
// Product Details
// Minimum amount is $0.50 US
$itemName = "Demo Product";
$itemNumber = "PN12345";
$itemPrice = 25;
$currency = "USD";
// Stripe API configuration
define('STRIPE_API_KEY', 'Your_API_Secret_key');
define('STRIPE_PUBLISHABLE_KEY', 'Your_API_Publishable_key');
// Database configuration
define('DB_HOST', 'MySQL_Database_Host');
define('DB_USERNAME', 'MySQL_Database_Username');
define('DB_PASSWORD', 'MySQL_Database_Password');
define('DB_NAME', 'MySQL_Database_Name');3.数据库连接dbConnect.php
<?php
// Connect with the database
$db = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);
// Display error if failed to connect
if ($db->connect_errno) {
printf("Connect failed: %s\n", $db->connect_error);
exit();
}4.结算表单页index.php
<?php
// Include configuration file
require_once 'config.php';
?>
<!DOCTYPE html>
<html>
<head>
<title>check out</title>
<!-- Stylesheet file -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
<!-- Stripe JS library -->
<script src="https://js.stripe.com/v3/"></script>
</head>
<body>
<div class="panel">
<div class="panel-heading">
<h3 class="panel-title">Charge <?php echo '$'.$itemPrice; ?> with Stripe</h3>
<!-- Product Info -->
<p><b>Item Name:</b> <?php echo $itemName; ?></p>
<p><b>Price:</b> <?php echo '$'.$itemPrice.' '.$currency; ?></p>
</div>
<div class="panel-body">
<!-- Display errors returned by createToken -->
<div id="paymentResponse"></div>
<!-- Payment form -->
<form action="payment.php" method="POST" id="paymentFrm">
<div class="form-group">
<label>NAME</label>
<input type="text" name="name" id="name" class="field" placeholder="Enter name" required="" autofocus="">
</div>
<div class="form-group">
<label>EMAIL</label>
<input type="email" name="email" id="email" class="field" placeholder="Enter email" required="">
</div>
<div class="form-group">
<label>CARD NUMBER</label>
<div id="card_number" class="field"></div>
</div>
<div class="row">
<div class="left">
<div class="form-group">
<label>EXPIRY DATE</label>
<div id="card_expiry" class="field"></div>
</div>
</div>
<div class="right">
<div class="form-group">
<label>CVC CODE</label>
<div id="card_cvc" class="field"></div>
</div>
</div>
</div>
<button type="submit" class="btn btn-success" id="payBtn">Submit Payment</button>
</form>
</div>
</div>
<script>
// Create an instance of the Stripe object
// Set your publishable API key
var stripe = Stripe('<?php echo STRIPE_PUBLISHABLE_KEY; ?>');
// Create an instance of elements
var elements = stripe.elements();
var style = {
base: {
fontWeight: 400,
fontFamily: 'Roboto, Open Sans, Segoe UI, sans-serif',
fontSize: '16px',
lineHeight: '1.4',
color: '#555',
backgroundColor: '#fff',
'::placeholder': {
color: '#888',
},
},
invalid: {
color: '#eb1c26',
}
};
var cardElement = elements.create('cardNumber', {
style: style
});
cardElement.mount('#card_number');
var exp = elements.create('cardExpiry', {
'style': style
});
exp.mount('#card_expiry');
var cvc = elements.create('cardCvc', {
'style': style
});
cvc.mount('#card_cvc');
// Validate input of the card elements
var resultContainer = document.getElementById('paymentResponse');
cardElement.addEventListener('change', function(event) {
if (event.error) {
resultContainer.innerHTML = '<p>'+event.error.message+'</p>';
} else {
resultContainer.innerHTML = '';
}
});
// Get payment form element
var form = document.getElementById('paymentFrm');
// Create a token when the form is submitted.
form.addEventListener('submit', function(e) {
e.preventDefault();
createToken();
});
// Create single-use token to charge the user
function createToken() {
stripe.createToken(cardElement).then(function(result) {
if (result.error) {
// Inform the user if there was an error
resultContainer.innerHTML = '<p>'+result.error.message+'</p>';
} else {
// Send the token to your server
stripeTokenHandler(result.token);
}
});
}
// Callback to handle the response from stripe
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
</script>
</body>
</html>5.服务端付款处理payment.php
<?php
// Include configuration file
require_once 'config.php';
$payment_id = $statusMsg = '';
$ordStatus = 'error';
// Check whether stripe token is not empty
if(!empty($_POST['stripeToken'])){
// Retrieve stripe token, card and user info from the submitted form data
$token = $_POST['stripeToken'];
$name = $_POST['name'];
$email = $_POST['email'];
// Include Stripe PHP library
require_once 'stripe-php/init.php';
// Set API key
\Stripe\Stripe::setApiKey(STRIPE_API_KEY);
// Add customer to stripe
try {
$customer = \Stripe\Customer::create(array(
'email' => $email,
'source' => $token
));
}catch(Exception $e) {
$api_error = $e->getMessage();
}
if(empty($api_error) && $customer){
// Convert price to cents
$itemPriceCents = ($itemPrice*100);
// Charge a credit or a debit card
try {
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $itemPriceCents,
'currency' => $currency,
'description' => $itemName
));
}catch(Exception $e) {
$api_error = $e->getMessage();
}
if(empty($api_error) && $charge){
// Retrieve charge details
$chargeJson = $charge->jsonSerialize();
// Check whether the charge is successful
if($chargeJson['amount_refunded'] == 0 && empty($chargeJson['failure_code']) && $chargeJson['paid'] == 1 && $chargeJson['captured'] == 1){
// Transaction details
$transactionID = $chargeJson['balance_transaction'];
$paidAmount = $chargeJson['amount'];
$paidAmount = ($paidAmount/100);
$paidCurrency = $chargeJson['currency'];
$payment_status = $chargeJson['status'];
// Include database connection file
include_once 'dbConnect.php';
// Insert tansaction data into the database
$sql = "INSERT INTO orders(name,email,item_name,item_number,item_price,item_price_currency,paid_amount,paid_amount_currency,txn_id,payment_status,created,modified) VALUES('".$name."','".$email."','".$itemName."','".$itemNumber."','".$itemPrice."','".$currency."','".$paidAmount."','".$paidCurrency."','".$transactionID."','".$payment_status."',NOW(),NOW())";
$insert = $db->query($sql);
$payment_id = $db->insert_id;
// If the order is successful
if($payment_status == 'succeeded'){
$ordStatus = 'success';
$statusMsg = 'Your Payment has been Successful!';
}else{
$statusMsg = "Your Payment has Failed!";
}
}else{
$statusMsg = "Transaction has been failed!";
}
}else{
$statusMsg = "Charge creation failed! $api_error";
}
}else{
$statusMsg = "Invalid card details! $api_error";
}
}else{
$statusMsg = "Error on form submission.";
}
?>
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<!-- Stylesheet file -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<div class="status">
<?php if(!empty($payment_id)){ ?>
<h1 class="<?php echo $ordStatus; ?>"><?php echo $statusMsg; ?></h1>
<h4>Payment Information</h4>
<p><b>Reference Number:</b> <?php echo $payment_id; ?></p>
<p><b>Transaction ID:</b> <?php echo $transactionID; ?></p>
<p><b>Paid Amount:</b> <?php echo $paidAmount.' '.$paidCurrency; ?></p>
<p><b>Payment Status:</b> <?php echo $payment_status; ?></p>
<h4>Product Information</h4>
<p><b>Name:</b> <?php echo $itemName; ?></p>
<p><b>Price:</b> <?php echo $itemPrice.' '.$currency; ?></p>
<?php }else{ ?>
<h1 class="error">Your Payment has Failed</h1>
<?php } ?>
</div>
<a href="index.php" class="btn-link">Back to Payment Page</a>
</div>
</body>
</html>
6. 样式文件 css/style.css
注意文件位置在css文件夹下面
/*.container{
padding: 20px;
}
h1{
color: #7a7a7a;
font-size: 28px;
text-transform: uppercase;
text-align: center;
}*/
#paymentFrm .row {
margin-right: 0;
margin-left: 0;
}
.panel {
width: 350px;
margin: 0 auto;
background-color: #fff;
border: 1px solid transparent;
border-radius: 4px;
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
border-color: #ddd;
}
.panel-heading {
padding: 10px 15px;
border-bottom: 1px solid transparent;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.panel > .panel-heading {
color: #333;
background-color: #f5f5f5;
border-color: #ddd;
}
.panel-title {
margin-top: 0;
margin-bottom: 10px;
font-size: 20px;
color: #333;
font-weight: 600;
}
.panel-body {
padding: 15px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: inline-block;
margin-bottom: 5px;
font-weight: bold;
}
.field {
display: block;
width: 100%;
height: 35px;
padding: 6px 12px;
font-size: 15px;
line-height: 1.2;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}
div.field{
padding-bottom: 0;
}
.field:focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
}
.row .left {
width: 45%;
float: left;
}
.row .right {
width: 35%;
float: right;
}
.right .field{
width: 75%;
}
.form-group iframe{
height: 30px !important;
}
.btn {
width: 100%;
padding: 10px 16px;
font-size: 18px;
line-height: 1.33;
border-radius: 6px;
border: none;
cursor: pointer;
}
.btn-success {
color: #fff;
background-color: #5cb85c;
border-color: #4cae4c;
}
.btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active {
color: #fff;
background-color: #47a447;
border-color: #398439;
}
#paymentResponse p{
font-size: 17px;
border: 1px dashed;
padding: 10px;
color: #EA4335;
margin-top: 0;
margin-bottom: 10px;
}
.status{
padding: 15px;
color: #000;
background-color: #f1f1f1;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
margin-bottom: 20px;
}
.status h1{
font-size: 1.8em;
}
.status h4{
font-size: 1.3em;
margin-bottom: 0;
}
.status p{
font-size: 1em;
margin-bottom: 0;
margin-top: 8px;
}
.btn-link{
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
padding: .375rem .75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
text-decoration: none;
}
.btn-link {
color: #007bff;
background-color: transparent;
border-color: #007bff;
}
.btn-link:hover, .btn-link:active, .btn-link:focus {
color: #fff;
background-color: #007bff;
border-color: #007bff;
text-decoration: none;
}
.success{
color: #34A853;
}
.error{
color: #EA4335;
}