LoginSignup
2
2

More than 3 years have passed since last update.

[Pay.jp] C#er pay.jp APIをたたく

Last updated at Posted at 2019-07-10

今日はウェブ決済サービスを提供するpay.jpのAPIをたたいてみます。管理画面からだと取引履歴が10件づつしか見れなく・・不便すぎて今ここにいます。。

オフィシャルドキュメントはこちら
https://pay.jp/docs/api/

決済一覧を取得

時間を指定して決済データ一覧を取得してみます。

こちらが決済データ一覧を取得するメソッドのドキュメント
https://pay.jp/docs/api/#%E6%94%AF%E6%89%95%E3%81%84%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E5%8F%96%E5%BE%97

リクエスト

API側に渡せるパラメターは、下記。

名前 タイプ 備考
imit Integer 取得するデータ数の最大値(1~100まで)。指定がない場合は 10 となる。
offset Integer 基準点からのデータ取得を行う開始位置。指定がない場合は 0 となる。
since Integer タイムスタンプ、指定したタイムスタンプ以降に作成されたデータのみ取得
until Integer タイムスタンプ、指定したタイムスタンプ以前に作成されたデータのみ取得
customer String 絞り込みたい顧客ID
subscription String 絞り込みたい定期課金ID

レスポンス

レスポンスオブジェクトは下記


public class Card
{
    public string address_city { get; set; }
    public string address_line1 { get; set; }
    public string address_line2 { get; set; }
    public string address_state { get; set; }
    public string address_zip { get; set; }
    public string address_zip_check { get; set; }
    public string brand { get; set; }
    public string country { get; set; }
    public int created { get; set; }
    public string cvc_check { get; set; }
    public object customer { get; set; }
    public int exp_month { get; set; }
    public int exp_year { get; set; }
    public string fingerprint { get; set; }
    public string id { get; set; }
    public string last4 { get; set; }
    public string name { get; set; }
    public string @object { get; set; }
}

public class Datum
{
    public int amount { get; set; }
    public int amount_refunded { get; set; }
    public bool captured { get; set; }
    public int captured_at { get; set; }
    public Card card { get; set; }
    public int created { get; set; }
    public string currency { get; set; }
    public string customer { get; set; }
    public string description { get; set; }
    public object expired_at { get; set; }
    public object failure_code { get; set; }
    public object failure_message { get; set; }
    public string id { get; set; }
    public bool livemode { get; set; }
    public object metadata { get; set; }
    public string @object { get; set; }
    public bool paid { get; set; }
    public object refund_reason { get; set; }
    public bool refunded { get; set; }
    public object subscription { get; set; }
}

public class TransactionList
{
    public int count { get; set; }
    public List<Datum> data { get; set; }
    public bool has_more { get; set; }
    public string @object { get; set; }
    public string url { get; set; }
}

オーソライズ、認証

PAY.JPのAPIを利用するには、ユーザー登録を行い、APIページからAPIキーを取得してください。 テスト用のキーでは、本番の支払い処理を行うサーバーへは接続されず、実際の支払い情報として計上されることもありません。本番用のキーは、本番申請を行うことで利用できるようになります。

とのことです。

パブリックキー HTML内に埋め込むトークン生成用のパブリックキー
シークレットキー サーバー側からBasic認証のユーザーネームとして渡すシークレットキー
通常の認証は、シークレットキーをユーザーネームとして扱い、Basic認証経由で行われます。パブリックキーは、あなたの決済画面のHTML内に組み込む公開用のAPIキーで、クレジットカードのトークンを生成する際に使用します。 シークレットキーは、全てのAPIリクエスト操作が可能となる重要なキーなので、くれぐれ も取扱いにご注意ください。

シークレットキーとパスワードをBasic認証経由で行うとのことで、下記のコードを用意

// Set Token
var base64authorization = Convert.ToBase64String(Encoding.ASCII.GetBytes(token + ":" + pass));

APIを叩いてみる

今回はシンプルにペラアプリケーションとしてHomeコントローラー/Indexビューページに作成しました。一度100件づつしか取得できないので、100以上ある場合はhas_moreがfalseになるまで100件づつ取得しています。あまり大量のデータを取得しようとするとPay.jpへの負担となるかもしれないので利用上の注意が必要です。今回は制限をかけていませんが、なにかしらの制限を設ける事をお勧めします。

/// <summary>
/// Get specific result
/// </summary>
/// <param name="token"></param>
/// <param name="pass"></param>
/// <param name="fromDateTime"></param>
/// <param name="toDateTime"></param>
/// <param name="inCsv"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> Index(string token, string pass, DateTime fromDateTime, DateTime toDateTime, bool inCsv)
{

  // Set Date, if not set
   if (fromDateTime <= DateTime.MinValue && toDateTime <= DateTime.MinValue)
   {
     fromDateTime = DateTime.UtcNow.AddHours(-4);
       toDateTime = DateTime.UtcNow.AddHours(-1);
   }

   // Set ViewData
   ViewData["FROMDATETIME"] = fromDateTime;
   ViewData["TODATETIME"] = toDateTime;
   ViewData["TOKEN"] = token;
   ViewData["PASS"] = pass;

   // Token must be set first
   if (string.IsNullOrEmpty(token))
   {
      return View();
   }

   // Token
   var payjpToken = token + ":" + pass;

   // Convert to DateTimeOffset
   var fromDateTimeOffset = new DateTimeOffset(fromDateTime.Ticks, new TimeSpan(+09, 00, 00));
   var toDateTimeOffset = new DateTimeOffset(toDateTime.Ticks, new TimeSpan(+09, 00, 00));

   // To Unix seconds
   var fromDateUnixSeconds = fromDateTimeOffset.ToUnixTimeSeconds();
   var toDateUnixSeconds = toDateTimeOffset.ToUnixTimeSeconds();

   // Set Path
   var path = "charges?";
   path += "since=" + fromDateUnixSeconds + "&until=" + toDateUnixSeconds;
   path += "&offset=0&limit=100";

   // Set Token
   var base64authorization = Convert.ToBase64String(Encoding.ASCII.GetBytes(payjpToken));

   // Call API
   var client = new HttpClient();
   var result = new ChargeListResponse();
   client.BaseAddress = new Uri(payJpBaseUrl);
   client.DefaultRequestHeaders.Accept.Clear();
   client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64authorization);

   HttpResponseMessage response = await client.GetAsync(path);

   // Make sure the call was a success
   if (response.IsSuccessStatusCode)
   {
     result = await response.Content.ReadAsAsync<ChargeListResponse>();
   }

   // see if there are more
   for (int i = 1; result.has_more; i++)
   {

     path = "charges?";
     path += "since=" + fromDateUnixSeconds + "&until=" + toDateUnixSeconds;
     path += "&offset=" + i * 100 + "&limit=100";

     response = await client.GetAsync(path);

     // Make sure the code was a success
     if (response.IsSuccessStatusCode)
     {
       var tresult = await response.Content.ReadAsAsync<ChargeListResponse>();

       // Add to result
       result.count += tresult.count;
       result.data.AddRange(tresult.data);
       result.has_more = tresult.has_more;
     }
   }

   client.Dispose();

   return View(result);
}

結果を表示する

Viewです。

@model ChargeListResponse
@{
    ViewData["Title"] = "レポート";
}
<div class="container-fluid py-2">
  <h1>@ViewData["Title"]</h1>
  <hr />
  @using (Html.BeginForm("Index", "PayJp", FormMethod.Post))
  {
    <div class="input-group mb-3">
      @Html.TextBox("token", ViewData["TOKEN"], new { @class = "form-control", @placeholder = "本番秘密鍵" })
      @Html.TextBox("pass", ViewData["PASS"], new { @type = "password", @class = "form-control", @placeholder = "パスワード" })
    </div>
    <div class="input-group mb-3">
      @Html.TextBox("fromDateTime", ViewData["FROMDATETIME"], new { @class = "form-control" })
      @Html.TextBox("toDateTime", ViewData["TODATETIME"], new { @class = "form-control" })
    </div>
    <div class="small">
      in JST
    </div>
    <div class="form-group">
      <button class="btn btn-primary rounded-0" type="submit">検索</button>
    </div>
}

<hr />
@if (Model != null)
{
  <div class="d-flex justify-content-between">
    <div>
      @Model.count<text>データ</text>
    </div>     
  </div>
  <hr />
  @if (Model.data != null)
  {
    <div class="table-responsive">
      <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th>Amount</th>
            <th>Captured</th>
            <th>Captured_at</th>
            <th>Card.address_city</th>
            <th>Card.address_line1</th>
            <th>Card.address_line2</th>
            <th>Card.address_state</th>
            <th>Card.address_zip</th>
            <th>Card.address_zip_check</th>
            <th>Card.brand</th>
            <th>Card.country</th>
            <th>Card.created</th>
            <th>Card.customer</th>
            <th>Card.cvc_check</th>
            <th>Card.exp_month</th>
            <th>Card.exp_year</th>
            <th>Card.fingerprint</th>
            <th>Card.id</th>
            <th>Card.name</th>
            <th>Card.last4</th>
            <th>Created</th>
            <th>Currency</th>
            <th>Customer</th>
            <th>Description</th>
            <th>Expired_at</th>
            <th>Failure_code</th>
           <th>Failure_message</th>
            <th>Id</th>
            <th>Livemode</th>
            <th>Metadata</th>
            <th>Paid</th>
            <th>Refunded</th>
            <th>Refund_reason</th>
            <th>Subscription</th>
          </tr>
        </thead>
        <tbody>
          @foreach (var record in Model.data)
          {
            <tr>
              <td>@record.amount</td>
              <td>@record.captured</td>
              <td>@record.captured_at</td>
              <td>@record.card.address_city</td>
              <td>@record.card.address_line1</td>
              <td>@record.card.address_line2</td>
              <td>@record.card.address_state</td>
              <td>@record.card.address_zip</td>
              <td>@record.card.address_zip_check</td>
              <td>@record.card.brand</td>
              <td>@record.card.country</td>
              <td>@record.card.created</td>
              <td>@record.card.customer</td>
              <td>@record.card.cvc_check</td>
              <td>@record.card.exp_month</td>
              <td>@record.card.exp_year</td>
              <td>@record.card.fingerprint</td>
              <td>@record.card.id</td>
              <td>@record.card.name</td>
              <td>@record.card.last4</td>
              <td>@record.created</td>
              <td>@record.currency</td>
              <td>@record.customer</td>
              <td>@record.description</td>
              <td>@record.expired_at</td>
              <td>@record.failure_code</td>
              <td>@record.failure_message</td>
              <td>@record.id</td>
              <td>@record.livemode</td>
              <td>@record.metadata</td>
              <td>@record.paid</td>
              <td>@record.refunded</td>
              <td>@record.refund_reason</td>
              <td>@record.subscription</td>
            </tr>
          }
        </tbody>
      </table>
    </div>
    }
  }
  else
  {
    <div>
      本番秘密鍵とパスワードを入力してください。
    </div>
  }
</div>

おわり

一旦以上です。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2