#交易状态 #支付 #API客户端 #Snap #重定向 #HTTP客户端 #midtrans

midtransclient

Midtrans支付API的非官方Rust API客户端/库

3个版本

0.1.2 2022年11月18日
0.1.1 2022年11月18日
0.1.0 2022年11月17日

#111 in 财务

MIT许可证

110KB
2K SLoC

Midtrans客户端 - Rust

crates.io license

这是Midtrans支付API的非官方Rust API客户端/库。

1. 安装

将以下行添加到您的Cargo.toml文件中

midtransclient = "X.X.X"

或者使用cargo

cargo add midtransclient

2. 使用

2.1 选择产品/方法

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(&parameters).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