使用Stripe国际贸易收款(PHP第一版教程)

后端开发PHP 806

写在前面:

如果你的网站在线业务面向海外的话避免不了用户支付的问题,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

用于测试支付的一些卡号:

  • 4242424242424242Visa
  • 4000056655665556Visa (debit)
  • 5555555555554444Mastercard
  • 5200828282828210Mastercard (debit)
  • 378282246310005American Express
  • 6011111111111117Discover

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;
}

Post Comment