領収証発行サービス
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

338 lines
8.8KB

  1. <?php
  2. namespace App\Logic\SMS;
  3. use App\Codes\SMSProviderName;
  4. use App\Codes\SMSSendPurpose;
  5. use App\Models\ReceiptIssuingOrder;
  6. use App\Models\SMSProvider;
  7. use App\Models\SMSProviderFourSMessageCommunication;
  8. use App\Models\SMSSendOrder;
  9. use App\Util\DateUtil;
  10. use Exception;
  11. use Illuminate\Http\Client\Response;
  12. use Illuminate\Support\Collection;
  13. use Illuminate\Support\Facades\Http;
  14. use LogicException;
  15. class FourSMessageManager implements SMSManager
  16. {
  17. private const SEND_URL = ['POST', "https://4sm.jp/api/sms_send"];
  18. private const CANCEL_URL = ['POST', "https://4sm.jp/api/sms_cancel"];
  19. private const RESULT_URL = ['POST', "https://4sm.jp/api/get_send_result"];
  20. private static function getUserId(): string
  21. {
  22. $ret = config("logic.sms.FourSMessage.userId");
  23. if ($ret === null) {
  24. throw new LogicException("SMS FourSMessage UserID 未定義");
  25. }
  26. return $ret;
  27. }
  28. private static function getPassword(): string
  29. {
  30. $ret = config("logic.sms.FourSMessage.password");
  31. if ($ret === null) {
  32. throw new LogicException("SMS FourSMessage Password 未定義");
  33. }
  34. return $ret;
  35. }
  36. public static function makeSMSSendOrder(ReceiptIssuingOrder $receiptIssuingOrder, SMSSendPurpose $purpose, string $contents): SMSSendOrder
  37. {
  38. $order = new SMSSendOrder();
  39. $order->receipt_issuing_order_id = $receiptIssuingOrder->id;
  40. $order->contract_id = $receiptIssuingOrder->contract_id;
  41. $order->phone_number = $receiptIssuingOrder->sms_phone_number;
  42. $order->summary_key1 = $receiptIssuingOrder->summary_key1;
  43. $order->summary_key2 = $receiptIssuingOrder->summary_key2;
  44. $order->purpose = $purpose;
  45. $order->content = $contents;
  46. return $order;
  47. }
  48. public static function getProvider(): SMSProvider
  49. {
  50. return SMSProvider::whereProviderName(SMSProviderName::FOUR_S_MESSAGE->value)->firstOrFail();
  51. }
  52. public function __construct(
  53. private SMSSendOrder $order,
  54. ) {
  55. }
  56. public function loadOrder(string $id): static
  57. {
  58. $this->order = SMSSendOrder::findOrFail($id);
  59. return $this;
  60. }
  61. public function setOrder(SMSSendOrder $order): static
  62. {
  63. $this->order = $order;
  64. return $this;
  65. }
  66. public function getOrder()
  67. {
  68. return $this->order;
  69. }
  70. public function send(): bool
  71. {
  72. $sendContents = [
  73. 'cp_userid' => static::getUserId(),
  74. 'cp_password' => static::getPassword(),
  75. 'carrier_id' => '99',
  76. 'message' => $this->order->content,
  77. 'address' => $this->order->phone_number,
  78. 'urlshorterflg' => '1',
  79. ];
  80. $this->order->setSMSProvider(static::getProvider());
  81. $res = $this->communication(self::SEND_URL, $sendContents);
  82. if ($res->isSuccess()) {
  83. // 送信依頼を作成
  84. $this->order->save();
  85. $comm = new SMSProviderFourSMessageCommunication();
  86. $comm->sms_send_order_id = $this->order->id;
  87. $comm->contract_id = $this->order->contract_id;
  88. $comm->request_id = $res->requestId;
  89. $comm->request_date = $res->requestDate;
  90. $comm->save();
  91. return true;
  92. } else {
  93. return false;
  94. }
  95. }
  96. public function poll(): bool
  97. {
  98. $requestId = SMSProviderFourSMessageCommunication::whereSmsSendOrderId($this->order->id)
  99. ->firstOrFail()
  100. ->request_id;
  101. $sendContents = [
  102. 'request_id' => $requestId,
  103. 'user_id' => static::getUserId(),
  104. 'password' => base64_encode(static::getPassword()),
  105. ];
  106. $res = $this->communication(self::RESULT_URL, $sendContents);
  107. if ($res->isSuccess()) {
  108. $res->save();
  109. }
  110. return $res->isSuccess();
  111. }
  112. public function cancel(): bool
  113. {
  114. $requestId = SMSProviderFourSMessageCommunication::whereSmsSendOrderId($this->order->id)
  115. ->firstOrFail()
  116. ->request_id;
  117. $sendContents = [
  118. 'request_id' => $requestId,
  119. 'cp_userid' => static::getUserId(),
  120. 'cp_password' => static::getPassword(),
  121. ];
  122. $res = $this->communication(self::CANCEL_URL, $sendContents);
  123. return $res->isSuccess();
  124. }
  125. private function communication(array $urlDef, array $sendData): CommunicationData
  126. {
  127. $method = $urlDef[0];
  128. $form = Http::withHeaders([
  129. 'Content-Type' => 'application/x-www-form-urlencoded',
  130. ])
  131. ->asForm();
  132. if ($method === 'GET') {
  133. $query = "";
  134. foreach ($sendData as $key => $value) {
  135. if ($query !== "") {
  136. $query .= '&';
  137. }
  138. $query .= $key . "=" . $value;
  139. }
  140. $url = $urlDef[1] . '?' . $query;
  141. $res = $form->get($url);
  142. } else {
  143. $url = $urlDef[1];
  144. $res = $form->post($url, $sendData);
  145. }
  146. logger(['SMS-SendContet' => $sendData, 'URL' => $url]);
  147. if (!$res->ok()) {
  148. $res->throw();
  149. }
  150. logger(['SMS-ReveiveContet' => $res->json()]);
  151. $ret = new CommunicationData($res, $this->order);
  152. return $ret;
  153. }
  154. }
  155. class CommunicationData
  156. {
  157. public string|null $result;
  158. public string|null $requestId;
  159. public string|null $requestDate;
  160. public string|null $errorCode;
  161. public string|null $errorMessage;
  162. /**
  163. * @var Collection<SMSProviderFourSMessageCommunication>
  164. */
  165. public Collection $data;
  166. private SMSSendOrder $order;
  167. private const RESULT_SUCCESS = 'SUCCESS';
  168. private const DATE_FORMAT = 'YmdHis';
  169. public function __construct(Response $res, SMSSendOrder $order)
  170. {
  171. $this->order = $order;
  172. $data = $res->json();
  173. $response = data_get($data, 'response');
  174. if ($response !== null) {
  175. $data = $response;
  176. }
  177. $this->result = data_get($data, 'result');
  178. $this->requestId = data_get($data, 'request_id');
  179. $this->requestDate = data_get($data, 'request_date');
  180. $this->errorCode = data_get($data, 'error_code');
  181. $this->errorMessage = data_get($data, 'error_message');
  182. $this->data = collect();
  183. $records = data_get($data, 'records');
  184. if (is_array($records)) {
  185. foreach ($records as $record) {
  186. $requestId = data_get($record, 'request_id');
  187. // 空文字の場合があるので、数値にキャストする
  188. $record['success_count'] = intVal($record['success_count']);
  189. $record['message_count'] = intVal($record['message_count']);
  190. $ele = SMSProviderFourSMessageCommunication::whereRequestId($requestId)
  191. ->firstOrFail();
  192. $ele->fill($record);
  193. $ele->sms_send_order_id = $order->id;
  194. $this->data->push($ele);
  195. }
  196. }
  197. }
  198. public function isSuccess(): bool
  199. {
  200. return $this->result === self::RESULT_SUCCESS;
  201. }
  202. public function save()
  203. {
  204. foreach ($this->data as $data) {
  205. $data->save();
  206. if ($this->isFixRecord($data)) {
  207. $this->order->done = true;
  208. if ($data->deliv_rcpt_date !== null) {
  209. $this->order->send_datetime = DateUtil::parse($data->deliv_rcpt_date, self::DATE_FORMAT);
  210. }
  211. if ($data->success_count > 0) {
  212. $this->order->cost = $this->calcCost($data);
  213. }
  214. }
  215. }
  216. }
  217. public function isFixRecord(SMSProviderFourSMessageCommunication $comm): bool
  218. {
  219. // 以下のコードになれば「状態確定」のタイミングであると判断できます。
  220. //  ・受付状態 : 90受付取消、または99受付失敗
  221. //  ・送信エラー: 31~35送信エラー、または40送達済
  222. if (in_array($comm->request_status, ['90', '99'])) {
  223. return true;
  224. }
  225. if (in_array($comm->sending_status, [
  226. '31',
  227. '32',
  228. '33',
  229. '34',
  230. '35',
  231. '40',
  232. ])) {
  233. return true;
  234. }
  235. return false;
  236. }
  237. private function calcCost(SMSProviderFourSMessageCommunication $comm): int
  238. {
  239. $provider = self::getMst();
  240. return $comm->success_count * $provider->cost;
  241. }
  242. private static function getMst(): SMSProvider
  243. {
  244. static $provider = null;
  245. if ($provider === null) {
  246. $provider = SMSProvider::whereProviderName(SMSProviderName::FOUR_S_MESSAGE->value)
  247. ->firstOrFail();
  248. }
  249. return $provider;
  250. }
  251. }