3个版本
0.1.2 | 2022年11月18日 |
---|---|
0.1.1 | 2022年11月18日 |
0.1.0 | 2022年11月17日 |
#111 in 财务
110KB
2K SLoC
Midtrans客户端 - Rust
这是Midtrans支付API的非官方Rust API客户端/库。
1. 安装
将以下行添加到您的Cargo.toml文件中
midtransclient = "X.X.X"
或者使用cargo
cargo add midtransclient
2. 使用
2.1 选择产品/方法
- Snap - 可定制的支付弹出窗口将出现在 您的web/app 上(无重定向)。 文档参考
- Snap重定向 - 客户需要被重定向到由midtrans托管的中支付url。 文档参考
- 核心API(VT-Direct) - 基本的后端实现,您可以像您喜欢的那样自定义嵌入在 您的web/app 上的前端(无重定向)。 文档参考
2.2 客户端初始化和配置
从 Midtrans仪表板 获取您的客户端密钥和服务器密钥
创建API客户端对象
// Create Core API instance
let core = CoreApi::new(false, "YOUR_SERVER_KEY".to_string())
.client_key("YOUR_CLIENT_KEY".to_string())
.build()
.unwrap();
// Create Snap API instance
let snap = Snap::new(false, "YOUR_SERVER_KEY")
.client_key("YOUR_CLIENT_KEY")
.build()
.unwrap();
您也可以使用 core.set_api_config(...)
或 core.api_cofig.set_<fieldname>(...)
示例重新设置配置
let core = CoreApi::new(true, "SOME_KEY".to_string()).build().unwrap();
let new_config = ApiConfig::new(false, "SERVER_KEY".to_string()).build();
core.set_api_config(new_config);
// Or you can change spesific config
let snap = Snap::new(true, "SOME_KEY".to_string()).build().unwrap();
snap.api_config.set_is_production(false);
snap.api_config.set_client_key("CLIENT_KEY".to_string());
2.2.A Snap
您可以在这里查看Snap示例。
Snap
类的可用方法
pub fn create_transaction(&self, parameters: &str) -> MidtransResult
pub fn create_transaction_token(&self, parameters: &str) -> Result<Value, MidtransError>
pub fn create_transaction_redirect_url(&self, parameters: &str) -> Result<Value, MidtransError>
parameters
是JSON字符串,包含 SNAP参数
获取Snap令牌
// Create Snap API instance
let snap = Snap::new(false, "YOUR_SERVER_KEY")
.client_key("YOUR_CLIENT_KEY")
.build()
.unwrap();
// Prepare parameter
let parameters = r#"{
"transaction_details": {
"order_id": "test-transaction-123",
"gross_amount": 200000
}, "credit_card":{
"secure" : True
}
}"#;
let transaction = snap.create_transaction(parameters).unwrap();
let transaction_token = transaction["token"];
// alternative way to create transaction_token:
let transaction_token = snap.create_transaction_token(¶meters).unwrap();
当客户点击支付按钮时初始化Snap JS
将 PUT_TRANSACTION_TOKEN_HERE
替换为上面获得的 transaction_token
<html>
<body>
<button id="pay-button">Pay!</button>
<pre><div id="result-json">JSON result will appear here after payment:<br></div></pre>
<!-- TODO: Remove ".sandbox" from script src URL for production environment. Also input your client key in "data-client-key" -->
<script src="https://app.sandbox.midtrans.com/snap/snap.js" data-client-key="<Set your ClientKey here>"></script>
<script type="text/javascript">
document.getElementById('pay-button').onclick = function(){
// SnapToken acquired from previous step
snap.pay('PUT_TRANSACTION_TOKEN_HERE', {
// Optional
onSuccess: function(result){
/* You may add your own js here, this is just example */
document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
},
// Optional
onPending: function(result){
/* You may add your own js here, this is just example */
document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
},
// Optional
onError: function(result){
/* You may add your own js here, this is just example */
document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
}
});
};
</script>
</body>
</html>
实现通知处理器
2.2.B Snap重定向
获取支付页面的重定向URL
// Create Snap API instance
let snap = Snap::new(false, "YOUR_SERVER_KEY")
.client_key("YOUR_CLIENT_KEY")
.build()
.unwrap();
// Prepare parameter
let parameters = r#"{
"transaction_details": {
"order_id": "test-transaction-123",
"gross_amount": 200000
}, "credit_card":{
"secure" : True
}
}"#;
let transaction = snap.create_transaction(param).unwrap();
let transaction_redirect_url = transaction['redirect_url'];
// alternative way to create redirect_url
let transaction_redirect_url = snap.create_transaction_redirect_url(param).unwrap();
实现通知处理器
2.2.C 核心API(VT-Direct)
CoreApi
结构的可用方法
pub fn charge(&self, parameters: &str) -> MidtransResult
pub fn capture(&self, parameters: &str) -> MidtransResult
pub fn card_register(&self, parameters: &str) -> MidtransResult
pub fn card_token(&self, parameters: &str) -> MidtransResult
pub fn card_point_inquiry(&self, token_id: &str) -> MidtransResult
parameters
是JSON字符串,包含 SNAP参数
信用卡获取令牌
获取令牌应在前端处理,请参阅API 文档
信用卡收费
// Create Core API instance
let snap = CoreApi::new(false, "YOUR_SERVER_KEY")
.client_key("YOUR_CLIENT_KEY")
.build()
.unwrap();
// Prepare parameter
let parameters = r#"{
"payment_type": "credit_card",
"transaction_details": {
"gross_amount": 12145,
"order_id": "test-transaction-54321",
},
"credit_card":{
"token_id": "<CREDIT_CARD_TOKEN>",
"authentication": True
}
}"#;
// charge transaction
let charge_response = core.charge(param).unwrap();
println!("charge_response: {:?}", charge_response);
信用卡3DS身份验证
信用卡收费结果可能包含用于3DS身份验证的redirect_url
。3DS身份验证应在前端处理,请参阅API 文档
2.2.D 订阅API
信用卡订阅API
要使用信用卡订阅API,您首先需要获取一键保存的令牌,请参阅此文档。
当初始卡支付被接受时,您将收到作为响应一部分的saved_token_id
(也将可在HTTP通知的JSON中找到),请参阅此文档。
// Create Subscription API instance
let core = CoreApi::new(false, "SERVER_KEY")
.client_key("CLIENT_KEY")
.build()
.unwrap();
// Prepare parameter
let parameters = r#"{
"name": "SUBSCRIPTION-STARTER-1",
"amount": "100000",
"currency": "IDR",
"payment_type": "credit_card",
"token": "436502qFfqfAQKScMtPRPdZDOaeg7199",
"schedule": {
"interval": 1,
"interval_unit": "month",
"max_interval": 3,
"start_time": "2021-10-01 07:25:01 +0700"
},
"metadata": {
"description": "Recurring payment for STARTER 1"
},
"customer_details": {
"first_name": "John A",
"last_name": "Doe A",
"email": "[email protected]",
"phone": "+62812345678"
}
}"#;
let create_response = core.create_subscription(parameters).unwrap();
let subscription_id = create_response["id"].as_str().unwrap();
// Get subscription by subscription_id
let get_response = core.get_subscription(subscription_id).unwrap();
// Disable subscription by subscription_id
let disable_response = core.disable_subscription(subscription_id).unwrap();
// Enable subscription by subscription_id
let enable_response = core.enable_subscription(subscription_id).unwrap();
// Update subscription by subscription_id
let parameters = r#"{
"name": "SUBSCRIPTION-STARTER-1-UPDATE",
"amount": "100000",
"currency": "IDR",
"token": "436502qFfqfAQKScMtPRPdZDOaeg7199",
"schedule": {
"interval": 1
}
}"#;
let update_response = core.update_subscription(subscription_id, parameters).unwrap();
GoPay订阅API
要使用GoPay订阅API,您首先需要使用GoPay令牌化API将您的客户GoPay账户与GoPay账户相关联,请参阅此部分。
您将使用get_payment_account
API调用接收GoPay支付令牌
2.2.E 令牌化API
// Create Subscription API instance
let core = CoreApi::new(false, "SERVER_KEY")
.client_key("CLIENT_KEY")
.build()
.unwrap();
// Prepare parameter
// please redirect_url update with your redirect URL
let parameters = r#"{
"payment_type": "gopay",
"gopay_partner": {
"phone_number": "81234567891",
"country_code": "62",
"redirect_url": "https://mywebstore.com/gopay-linking-finish"
}
}"#;
// Link payment account
let link_response = core.link_payment_account(parameters).unwrap();
// Get payment account
let get_response = core.get_payment_account(active_account_id).unwrap();
// unlink payment account
let unlink_resposne = core.unlink_payment_account(active_account_id).unwrap();
2.3 处理HTTP通知
重要提示:为了在您的后端/数据库中更新交易状态,不要仅依赖前端回调!出于安全考虑,为确保状态确实来自Midtrans,仅根据HTTP通知或API获取状态来更新交易状态。
创建单独的Web端点(通知URL)以接收HTTP POST通知回调/钩子。每当交易状态更改时,都会发送HTTP通知
// Create Core API / Snap instance (both have shared `transactions` methods)
let core = CoreApi::new(false, "SERVER_KEY")
.client_key("CLIENT_KEY")
.build()
.unwrap();
let status_response = core.status(transaction_id).unwrap();
let notification_response = core.notification_from_json(status_response).unwrap();
println!(
"Transaction notification received. Order ID: {}. Transaction status: {}. Fraud status: {}",
notification_response["order_id"],
notification_response["transaction_status"],
notification_response["fraud_status"]
);
// Sample transaction_status handling logic
if transaction_status == "capture" {
if fraud_status == "challenge" {
// TODO set transaction status on your databaase to "challenge"
}
else if fraud_status == "accept" {
// TODO set transaction status on your databaase to"success"
}
} else if transaction_status == "cancel"
|| transaction_status == "deny"
|| transaction_status == "expire" {
// TODO set transaction status on your databaase to "failure"
} else if transaction_status == "pending" {
// TODO set transaction status on your databaase to "pending" / waiting payment
}
2.4 交易操作
获取状态
// Get status of transaction that already recorded on midtrans (already `charge`-ed)
let status_response = core.status(transaction_id).unwrap();
获取B2B状态
// Get transaction status of VA b2b transaction
let status_response = core.statusb2b(transaction_id).unwrap();
批准交易
// Approve a credit card transaction with `challange` fraud status
let status_response = core.approve(transaction_id).unwrap();
拒绝交易
// Deny a credit card transaction with `challange` fraud status
let status_response = core.deny(transaction_id).unwrap();
取消交易
// cancel a credit card transaction or pending transaction
let status_response = core.cancel(transaction_id).unwrap();
过期交易
// expire a pending transaction
let status_response = core.expire(transaction_id).unwrap();
退款交易
// refund a transaction (not all payment channel allow refund via API)
let parameters = r#"{
"refund_key": "order1-ref1",
"amount": 5000,
"reason": "Item out of stock"
}"#;
let status_response = core.refund(transaction_id, parameters).unwrap();
直接退款退款交易
// refund a transaction (not all payment channel allow refund via API) with Direct Refund
let parameters = r#"{
"refund_key": "order1-ref1",
"amount": 5000,
"reason": "Item out of stock"
}"#;
let status_response = core.refund_direct(transaction_id, parameters).unwrap();
3. 处理MidtransError
当在Midtrans API调用中使用返回Result<_, MidtransError>
的方法时:在core.charge(...)
或snap.create_transaction(...)
中,您可以这样处理:
let charge_response = core.charge(parameters);
match charge_response {
Ok(ref result) => println!("{:?}", result),
Err(ref err) => match err {
MidtransError::RequestError(e) => println!("{e}"),
MidtransError::JsonDecodeError(e) => println!("{e}"),
MidtransError::ParseError(e) => println!("{e}"),
MidtransError::ApiError(e) => println!("{e}")
}
};
4. 高级用法
自定义HTTP头
// Create Core API instance
let core = CoreApi::new(false, "SERVER_KEY")
.client_key("CLIENT_KEY")
.build()
.unwrap();
// Set custom HTTP header for every request from this instance
let mut custom_headers = HeaderMap::new();
custom_headers.insert("X-Custom-Header", "Some Value".parse().unwrap());
core.api_config.set_custom_headers(custom_headers);
重写/附加HTTP通知URL
如API文档中所述,商户可以选择在每个交易中更改或添加自定义通知URL。这可以通过向请求中添加额外的HTTP头来实现。
这可以通过以下方式实现:
// Create Snap instance
let snap = Snap::new(false, "SERVER_KEY")
.client_key("CLIENT_KEY")
.build()
.unwrap();
// set custom HTTP header that will be used by Midtrans API to override notification url
let mut custom_headers = HeaderMap::new();
custom_headers.insert("x-override-notification", "https://example.org".parse().unwrap());
snap.api_config.set_custom_headers(custom_headers);
// or append notification
let mut custom_headers = HeaderMap::new();
custom_headers.insert("x-append-notification", "https://example.org".parse().unwrap());
snap.api_config.set_custom_headers(custom_headers);
自定义HTTP代理
// Create Snap instance
let snap = Snap::new(false, "SERVER_KEY")
.client_key("CLIENT_KEY")
.build()
.unwrap();
let proxies = reqwest::Proxy::http("https://secure.example").unwrap();
snap.api_config.set_proxies(proxies);
在底层,此API包装器正在使用reqwest作为HTTP客户端。您可以在其文档中进一步了解代理。
获取帮助
依赖项
~6–20MB
~270K SLoC