PayPalのエクスプレスチェックアウトをRestAPIで再現する

### はじめに
公式サイトでは一見ClassicAPIの使用を勧めているように見えますが、DeveloperサイトからGithub行ってREADMEを見ると「ClassicAPIはもう使わなくなるからRestAPI使ってね」と書いてあります。

・・・すでに回りくどい書き方をしているので察しがつく方もいると思いますが、
**公式が非常にわかりにくい**
Webで調べてたら同じ意見が日英問わず出ているので、これはもう仕様です。動作確認するために公式のあちこちを参照しました。まとめといてくれよ・・・。誰かの参考になるかもしれないので技術メモを残します。

### アプリケーションを登録する
PayPalのRestAPIはOauth2.0を採用しています。Twitterでアプリを作ったことがある人はピンとくると思いますが、PayPalでもアプリの登録が必要です。以下のサイトから登録できます。
[https://developer.paypal.com/developer/applications](https://developer.paypal.com/developer/applications)
「あ？なんも出てこねーぞ？」って方、右上からログインしてください。
「あ？つくれねーぞ？」って方、sandboxで売り手アカウント作って下さい。

アプリを作ったら、`Client ID`と`Secret`を入手できるのでどこかにメモしておいてください。

### Java側の実装
まずはMavenでライブラリを取得。最新は1.4.1

```
<dependency>
  <groupId>com.paypal.sdk</groupId>
  <artifactId>rest-api-sdk</artifactId>
  <version>1.4.1</version>
</dependency>
```

実装の基本的な流れは公式のこちらのリンクを見ればわかります。
※ここにたどり着くのに1日かかった。

[https://devtools-paypal.com/guide/pay_paypal](https://devtools-paypal.com/guide/pay_paypal)
ちなみに補足なしではちゃんと動作しません。なので補足します。

#### Paymentを作って、客にPayPalで認証させる
仮に、あなたのサービスでお客さんがformが送信したとします。そして以下のコードでそれをキャッチしたとします。

```
Map<String,String> paypalConfig = new HashMap<String,String>();
paypalConfig.put("mode", env.getRequiredProperty("mode"));

String CLIENT_ID = "your.client.id";
String CLIENT_SECRET = "your.client.secret";
String CANCEL_URL = "your.cancel.url";
String RETURN_URL = "your.return.url";

String accessToken = new OAuthTokenCredential(CLIENT_ID, CLIENT_SECRET, paypalConfig).getAccessToken();
APIContext apiContext = new APIContext(accessToken);
apiContext.setConfigurationMap(paypalConfig);

Amount amount = new Amount();
amount.setCurrency("JPY");
amount.setTotal("400");

Transaction transaction = new Transaction();
transaction.setDescription("400 yen mo-rai!");
transaction.setAmount(amount);

List<Transaction> transactions = new ArrayList<Transaction>();
transactions.add(transaction);

Payer payer = new Payer();
payer.setPaymentMethod("paypal");

Payment payment = new Payment();
payment.setIntent("sale");
payment.setPayer(payer);
payment.setTransactions(transactions);
RedirectUrls redirectUrls = new RedirectUrls();
redirectUrls.setCancelUrl(CANCEL_URL);
redirectUrls.setReturnUrl(RETURN_URL);
payment.setRedirectUrls(redirectUrls);

Payment createdPayment = payment.create(apiContext);
for (Links links : createdPayment.getLinks()) {
  if (links.getRel().equals("approval_url")) {
    return "redirect:"+links.getHref();
  }
}
```

最後の`return "redicrect:"+links.getHref();`は私がspring bootでシミュレートしたからこうなっているだけで、ここは開発環境にあわせて変更してください。リダイレクトできれば何でもいいです。

このコードが実行されると、客は店のサイトからPayPalに移動します。そして、400円を店にあげるかどうかを聞かれるわけです。
※あくまでデモ

支払いの部分はPayPalがやってくれます。キャンセルにせよ、支払うにせよ、処理が終われば客はまた店のサイトに戻ってきます。
※実際に動かして、sandboxアカウントで支払ってみてください。

#### Paymentを確定する
ここでは客が支払ったとします。
PayPalからはGETで`paymentId`、`token`、`PayerID`を`RETURN_URL`に返してきます。そのあとの処理は

```
String accessToken = "さっきと一緒のやり方で取得";
APIContext apiContext = new APIContext(accessToken);
apiContext.setConfigurationMap(paypalConfig);

Payment payment = new Payment();
payment.setId(paymentId);
PaymentExecution paymentExecute = new PaymentExecution();
paymentExecute.setPayerId(payerID);
Payment executedPayment = payment.execute(apiContext, paymentExecute);
```

`execute`すると支払いが実行されます。
ちなみに、上記のコードはとりあえず動作を確認するためだけのものなので、実際に運用するとなると修正は必要です。


### 最後に
使い勝手で言ったら完全にWebPayの方がいいです。PayPalのドキュメントは本当に見づらい。ClassicAPIですら使い方がとっちらかってます。これは完全に妄想ですが、PayPalのドキュメントを見てると「プログラムを次々と書いてたらワケワカメになっちゃった。今どこで何が動いてるのかワカランチン。てへぺろ」という状況になっていないか心配です。公式ドキュメント（英語）ですら新旧の情報が入り混じっていて、しかもまとまっていないし、developerサイトとsandboxサイトで情報が共有されていないし・・・。これがプログラムでも起こっていたら、そのうち肝心のペイメントのところで大ポカやらかすんじゃないかと不安になりました。今考えているサービスでは店と客で双方向に送金できる方がありがたいのでPayPalを見てましたが、WebPay採用して店から客への送金部分は人力でやろうかなあ。