はじめに
APIを使う側の場合、サンプルコードなどがあれば、まずはそれに忠実な操作、処理をして疎通を確認する。
ライブラリは便利だが、ブラックボックスになる面もあるのでトラブルの原因把握や問題解決から遠くなる危険がある。
ソースが公開されているのなら、それを辿るがよろし。
何が起こったか?
某決済業者のAPIを使い銀行振込用のバーチャル口座のリクエストを送った時に、全角文字が含まれているとその部分だけ文字化けが発生した。
なお、クレジットカード払いなどの別手段では発生しなかった。
参考:当方サーバ側環境
- PHP7.X系
- Laravel5.X系
- guzzlehttp6.5.2
- システム内のデフォルトの文字コードはUTF-8
PHPやLaravelそのものは関係なくて、guzzleの中の問題(断言)
問題調査で発生したこと
途方に暮れる
文字コードをSJISに変換する必要がある
→変換しても、文字化けた後の内容が変わっただけで根本的な解決にはならなかった。
リクエストを送るときのContent-Typeが問題
→ Postmanなどの、API送信ツールを使ったが、文字化けしていた。
そのほか、URLエンコードした値を送信など様々な事を試みたがダメだった。
なお、業者がサンプルで提供している(本案件では採用しない)決済モジュールを動かしてみたら無事文字化けしなかった。
cURLで実装してみたら、問題なく送信できた!
決済モジュールのコードを辿ってみたら、全角文字列はSJISに変換する。
curl_setopt
で、様々な設定をしたのちリクエストを送信。
結局、いかにもありそうなcURLのサンプルコードを書いて動かしたら成功した。
//前提:こんな感じのリクエスト値が存在する(実運用時には、他のメソッドで加工するケースが多いと思います)
$params = [
'access_code' => 'unique_access_code',
'price' => '3980',
'customer_name' => mb_convert_encoding('バカ殿様','SJIS','UTF-8')
];
//POSTする値を
$post_fields = http_build_query($params);
// APIのリクエスト先URL。各自の環境に置き換えて考えてください
$request_url = "https://example.com/path/to/api/receiver";
//まず、オブジェクトを作る
$curl = curl_init();
//リクエスト形式、戻り値の存在、リクエスト先などを設定する。
//URLは適宜考えてください
curl_setopt($curl,CURLOPT_URL,$request_url);
curl_setopt($curl,CURLOPT_POST,true);
curl_setopt($curl,CURLOPT_RETURNNUMBER,true);
//リクエスト先の仕様にそろえて、URLエンコード済の値をセットする
curl_setopt($curl,CURLOPT_POSTFIELDS,$post_fields);
//ここから下、cURLのリクエスト送信とレスポンス
$response = curl_exec( $curl );
$curlinfo = curl_getinfo( $curl );
curl_close( $curl );
顧客名は、時事ネタです(合掌)
犯人は(たぶん)お前だ!
guzzlehttp/guzzle/src/Client.php
applyOptions
メソッド内にてhttp_build_query
を使っていたが
ここでクエリを編集する部分に問題があるようだ。
//
if (isset($options['query'])) {
$value = $options['query'];
if (is_array($value)) {
$value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);//この行に問題あり
}
if (!is_string($value)) {
throw new \InvalidArgumentException('query must be a string or array');
}
$modify['query'] = $value;
unset($options['query']);
}
PHPリファレンスのhttp_build_queryのページによれば 4つ目の引数enc_typeは
デフォルトでは PHP_QUERY_RFC1738
つまり、RFC1738 に従ってエンコードされる。でよかったにも関わらず 上記ソースの抜粋のようにPHP_QUERY_RFC3986
でエンコードされていた…これが原因のようだ。
参考
PHP:http_build_query