.NET MAUI × Stripe C#クロスプラットフォームで楽々クレジット決済

# はじめに
.NET MAUIでクレジットカード決済をやってみたかったので、いろいろ調べたらStripeのドキュメントがC#erに優しそうだったのでこれを使って.NET MAUIで実装してみました。

# 環境,注意点など
・localhostで試しました。(StripeはHttps接続しか認識しないようで、サーバーがHttpだと拒否されるらしいです)

・今回はMVVMToolKitを大活躍させて作成しました。プロパティ作るのすごい楽だねぇ

・localhostなのでAndroidでは試せず断念、Windowsのみ検証

# 手順
それでは早速クレジットカード決済のやり方について紹介します。

### Stripe登録
Stripeに一度ユーザー登録をする必要があります。
以下のリンクから手順に沿ってユーザー登録をしてください。

https://stripe.com/jp

![登録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/2f1dd304-9afb-7466-1fd8-633926cc6d37.png)

ホーム画面に行きますと、右側の小さいところにStripeを使うためのAPIキーが表示されています。

![APIKey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/a04da0a4-96be-4b4a-cfa2-6498aaabdd62.png)

本番用のAPIキーは別途申請して作らなくてはいけないのですが、テスト用のAPIキーは何もせずともここに表示されます。
これを使って今回はテスト環境でのクレジットカード決済を行っていきます。

### Stripe.netをNuGet
NuGetからStripe.netをインストール
![stripenet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/cd75d607-7a00-b52f-6e7d-e2fd5d7b5be2.png)


### メイン画面
サンプルプロジェクトのボタンを「1500円支払い」に変えてこのボタンを押すと、クレジットカードを登録し、支払うという形で行っていきます。
また、支払い結果を表示するためにLabelのTextをバインドしています。
MainPage
```xml
    <ContentPage.BindingContext>
        <local:MainViewModel/>
    </ContentPage.BindingContext>
            <Label Text="{Binding Result}"/>//←結果の文字をバインディングしている。起動時は空白

            <Button　//←いつものサンプル画面のボタン、Commandでクレジットカード支払いの画面に遷移する
                x:Name="Payment"
                Text="1500円支払う"
                SemanticProperties.Hint="Counts the number of times you click"
                HorizontalOptions="Center"
                Command="{Binding GotoPaymentCommand}"/>
```
MainViewModel
```cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace StripeforMAUI.ViewModel
{
    [QueryProperty(nameof(Result),"post")]//←結果がクレジット決済画面から結果が変えるとこのページに遷移するようになっているので、
    public partial class MainViewModel:ObservableObject//Resulプロパティを作ってそこに渡している(Key名は"post")
    {
        [ObservableProperty]
        string result;//結果を表示するためのプロパティ（MvvmTKを使っているので、OnPropertyChanged()までの処理を書かなくていい、すごい楽）
        [RelayCommand]
        void GotoPayment()
        {
            Shell.Current.GoToAsync(nameof(View.CreditPage));//←クレジット決済画面に遷移
        }
    }
}
```
AppShellの画面を登録することを忘れないように
```cs
	public AppShell()
	{
		InitializeComponent();
        //↓クレジット決済画面の設定
		Routing.RegisterRoute(nameof(View.CreditPage), typeof(View.CreditPage));
	}
```

### クレジット決済画面
画面は入力によってカードに数字が表れていくやつ作ったけどとりあえず最低限はこれ
CreditPage
```xml
            <Entry Margin="12" //カード番号を入力するエントリー
                   Placeholder="CardNumber" 
                   Text="{Binding CardNumber}"/>
            <HorizontalStackLayout>
                <Entry Margin="12" //有効期限Monthを入力
                   Text="{Binding ActiveMonth}"/>
                <Entry Margin="12" //有効期限Yearを入力
                       Text="{Binding ActiveYear}"/>
                <Entry Margin="12" //CVCというカードの裏面のセキュリティナンバーを入力
                       Text="{Binding CVV}"/>
            </HorizontalStackLayout>
            <Entry Margin="12" //カードの名義を入力
                   Placeholder="CustomerName"
                   Text="{Binding CardName}"/>
            <Button Text="Go" //決済ボタン、このボタンを押すとWebクレジット決済が行われる
                    Command="{Binding CreateAccountCommand}"/>
```
そしてここが大きな山場となる。
CreditViewModel
```cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Newtonsoft.Json;
using Stripe;
using StripeforMAUI.Model;

namespace StripeforMAUI.ViewModel
{
    public partial class NewAccountModel:ObservableObject
    {
        [ObservableProperty]
        string cardNumber;//←CardNumberプロパティを作成しているカード番号
        [ObservableProperty]
        string activeMonth;//←ActiveMonthプロパティ、有効期限の月(intではなくてstringにした方が取り回しがしやすい(04とかできるため))
        [ObservableProperty]
        string activeYear;//←ActiveYearプロパティ、有効期限の年
        [ObservableProperty]
        string cVC;//CVCプロパティ、カード裏面のセキュリティコード
        [ObservableProperty]
        string cardName;//←カードの名義
        [ObservableProperty]
        string chargeToken;//←クライアント(MAUI)側で作成されるチャージに必要なトークン番号(ObservablePropertyにしなくてよかったかも、、、)
        [ObservableProperty]
        string postResult;//←APIから結果を返した文字を受け取るプロパティ(ObservablePropertyにしなくてよかったかも、、、)

        [RelayCommand]
        void CreateAccount()//←カプセル化してるだけ
        {
            ChargeToken=CreateToken();
            StripePayment();
        }
        //決済をするのにクライアントで入力したカード情報をひとまずトークンに変換して、そのトークンでやり取りするのでまずはトークンの作成から
        public string CreateToken()
        {                                    //↓Stripe公式から得られた公開可能キーを入力(txt,jsonファイルから読み取る形でもよいかも)
            StripeConfiguration.ApiKey = "pk_test_...";
            var tokenOptions = new TokenCreateOptions()
            {
                Card = new TokenCardOptions()//←StripeのAPIで用意されているプロパティにこちらが入力したプロパティを入れていく
                {
                    Number = CardNumber,//カード番号
                    ExpMonth = ActiveMonth,//有効期限の月
                    ExpYear = ActiveYear,//有効期限の年
                    Cvc = CVC,//セキュリティナンバー
                }
            };
        var service = new TokenService();
        Token stripetoken = service.Create(tokenOptions);//←（）の中に上で作ったクレカのオプションを入れると勝手にトークンが出来上がる。
        return  stripetoken.Id;//←そのトークンのIdプロパティだけ入手する。これを上のRelayCommandではChageTokenプロパティに代入している
        }

        public async void StripePayment()
        {
            HttpClient client= new HttpClient();
            //↓サーバーAPIに作り出したトークン情報などを渡す。もちろん自分のサーバーを立てる必要がある。
            var result =await client.PostAsync("https://localhost:7170/api/payment",
                new StringContent(JsonConvert.SerializeObject(new PaymentModel()
                {
                    Amount = 1500,//←買ったものの金額（直書きでごめんなさい、、、）
                    Token = ChargeToken//先ほどのメソッドで作り出したトークンをプロパティに渡している。
                }),
                Encoding.UTF8,"application/json"));

            PostResult = $"支払い:{result.StatusCode}";//APIから帰ってきた情報をPostResultプロパティに入れる
            await Shell.Current.GoToAsync($"..?post={PostResult}");//メインページに戻り、PostResultのデータもキー名"post"として渡す
        }
    }
}
```

### API用サーバーを立てる
APIサーバーを同じソリューションで立ち上げ、Stripeにクレカ決済を行ってもらう。
C#なのでフルスタック簡単ですね。ここがSwift,Kotlin,Dart,JavaScript(node.jsあるか、、、)と違うところ

ソリューションエクスプローラーからソリューションを右クリックして「追加」→「新しいプロジェクト」を作成します。
![projekuttuika.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/121adef1-8295-f3d7-e938-8d40fbc0bde0.png)

今回はASP.NET Core WebAPIテンプレートから作成しました。
![WebAPI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/41803b15-4f8b-61d1-b1f2-6f6172b245d3.png)

最初にデータを入れるためのクラスを作りました。
```cs
    public class PaymentModel
    {
        public string? Token { get; set; }
        public decimal Amount { get; set; }
    }
```
そしてソリューションエクスプローラーから「Controllers」→「追加」→「コントローラー」で新しいコントローラーを作ります。
![コントローラー作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/7f8c1318-3505-bbed-e00c-3f5d168610ad.png)

そして、左側のメニューから「MVC」「読み取り/書き込みアクションがあるMVCコントローラー」を追加します。
![テンプレート選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/c450f6d9-229f-8dc0-3625-96216a947f3e.png)
最初に生成されるコードは邪魔なので削除しておきます。

そして以下のような形で処理を書いていきます。
```cs
using Stripe;
using StripeAPIs.Model;

namespace StripeAPIs.Controllers
{
    [Route("api/[controller]")]//←APIを作成します。これだけでAPI作成できるのマジ簡単で最強だと思う
    public class PaymentController : Controller//上の[controller]ってのがこのクラス名からControllerを削除したものになる。この場合は"api/payment"
    {   
        //↓これだけでPOST処理できるのすごくねぇか？
        [HttpPost]                                         //↓クライアント(MAUI)側から送られてきたデータはpaymentに入ります。
        public IActionResult Post([FromBody] PaymentModel payment)
        {
            //クライアントから送られてきたデータをchargeにいれます。金額や単位などを入れて決済を行う環境を作る
            var charge = new ChargeCreateOptions
            {
                Amount = Convert.ToInt32(payment.Amount),//(金額、クライアント側からは文字で送られてくるため数字に変換)
                Currency = "jpy",//(単位jpyは日本円です。)
                Description = "something awesome",//よくわからんがなんか素晴らしい。
                Source = payment.Token//クライアント側で作ったクレジットカードのトークン情報
            };
                                         //↓APIキーを記入、この部分は秘密キー(下側)のAPIキーになります。間違えないように。
            StripeConfiguration.ApiKey="sk_test_...";//appsettings.jsonから読み取る形でもよい

            try
            {
                //Stripeが勝手に決済処理をやってくれます。
                var service = new ChargeService();
                var chargeService = service.Create(charge);
                return Ok(true);
            }
            catch (StripeException ex)//例外処理もそれ用のメソッドがあるみたいです。
            {
                StripeError stripeError = ex.StripeError;
                return BadRequest(stripeError);
            }
        }
    }
}
```
最初にAPIサーバーの方を先にデバッグ無しで立ち上げ、アプリ側の方をデバッグしてみます。
![deage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/81d49b82-c145-d3dc-d98a-b2df2602276c.png)

### テストカードについて
テスト用にクレジットカードのテスト番号が書かれたものがありますので、そちらを入力に使ってください。
カード番号が正しくないとエラー吐きます(この例外処理やってない、、、(´;ω;｀))

https://stripe.com/docs/testing

結果はこんな感じです。
![無題の動画-_2_.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3007605/29adfaf6-c07c-874f-79a1-affcb5866598.gif)

# さいごに
.NET MAUIでクレジットカード決済ができるようになりました。
C#独自というかフルスタックでいろいろなAPI扱えるの便利ですね。。。

今回localhostで試したのでAndroidで試せませんでした。ほかの端末でする場合はマニフェストも追加しないといけなくなると思います。

今回自分でカード画面作りましたがこんな感じのもの作っている人がいるので、そのままコピペして借りてきてもよかったかもしれません。
![GitHub](https://raw.githubusercontent.com/DamianSuess/Learn.MauiPaymentUi/master/SampleCards.gif)
こっちは入力した文字によってクレカ会社のアイコンやバックカラーが変わったりデフォルトで入力欄のハイフンがついてくるのでこっちの方がよさそう

https://github.com/DamianSuess/Learn.MauiPaymentUi