使用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
–Visa
4000056655665556
–Visa (debit)
5555555555554444
–Mastercard
5200828282828210
–Mastercard (debit)
378282246310005
–American Express
6011111111111117
–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; }