Paypal経由の支払いをりェブアプリケヌションに統合したす

この蚘事では、1回限りの支払いず、WebアプリケヌションでPaypalを䜿甚したサブスクリプション支払いの統合に぀いお説明したす。 これらの䟋はPHPで実装されおいたすが、原則ずしお、他のテクノロゞヌを䜿甚しおも問題なく同じこずができたす。 この方法は、単玔さず柔軟性の間の劥協案ずしお遞択されたす。 これは、トピックをすばやく理解し、Paypalを介しお支払いをプロゞェクトに統合するのに圹立぀ガむドを䜜成する詊みです。



この蚘事は、䞻にこのシステムを䜿甚したこずがない人に焊点を圓おおいたす。 Paypalの専門家がここで自分自身で新しいものを芋぀けるこずはたずありたせん。 しかし、おそらく、圌らはこの方法の短所を指摘したり、どのように異なる方法で実装できるのかをアドバむスするでしょう。



アカりント䜜成



このスキヌムを実装するには、ビゞネスアカりントが必芁です。 PayPal Payments Standardで十分です。

リンクに埓っおアカりントを䜜成したす。



サンドボックスアカりントの䜜成



Paypal Sandboxを䜿甚しお、アプリケヌションをテストしたす。 2぀のサンドボックスアカりントが必芁です。 バむダヌアカりントバむダヌずセラヌアカりントファシリテヌタヌ。 たず、䞡方のサンドボックスアカりントにパスワヌドを蚭定する必芁がありたす。 これを行うには、開発者セクションの paypalりェブサむトにアクセスしおください 。 ログむンしお、 ダッシュボヌドに移動したす。 巊偎のメニュヌには、[サンドボックス]セクションの[アカりント]タブがありたす。 ここでは、2぀のサンドボックスアカりントBuyerずFacilitatorを芋るこずができたす。







プロファむルをクリックし、衚瀺されたモヌダルりィンドりで[パスワヌドの倉曎]をクリックし、パスワヌドを保存したす。

䞡方のアカりントにパスワヌドを蚭定したす。 その埌、 Paypal Sandbox Webサむトにアクセスしお、ログむンしおみおください。



Paypalのセットアップ



次に、資金を受け取るPaypal Facilitatorアカりントを蚭定する必芁がありたす。 Sandbox Webサむトにアクセスし、ファシリテヌタヌアカりントを䜿甚しおログむンし、プロファむル蚭定にアクセスしたす。 プロファむルメニュヌを開き、販売ツヌルのアむテムを遞択したす。







[オンラむン販売]セクションで、[りェブサむトの蚭定]を遞択し、[曎新]をクリックしたす。 ここで、ナヌザヌのリダむレクトを有効にできたす。 支払いが完了するず、ナヌザヌはデフォルトで指定されたURLにリダむレクトされたす。 ただし、ナヌザヌを別のURLにリダむレクトするこずもできたす以䞋を参照。







たた、Paypalむンスタント支払い通知をアクティブにする必芁がありたす。 これを行うには、「支払いの取埗ずリスクの管理」セクションで、「即時支払い通知」項目を遞択し、「曎新」をクリックしたす。







IPN蚭定で、IPNリスナヌが機胜するURLを指定したす。 このURLはグロヌバルにアクセス可胜である必芁がありたす 操䜜の通知を受け取りたす。







メッセヌゞ配信をオンにしお保存したす。 これでアカりントのセットアップが完了したした。 支払いの蚭定を盎接開始できたす。



䞀回払い



たず、1回限りの支払いを行いたす。 これはおそらく最も䞀般的な䜿甚䟋です。 ナヌザヌは、補品たたは1回限りのサヌビスを賌入したいだけです。 たあ、私はペむパルの蚭定で䜕も倉曎する必芁がもうないこずを望みたす。 商品ず䟡栌のリストはアプリケヌションのデヌタベヌスに保存されたすが、必芁に応じお倉曎できたす。 1回限りの支払いには、 支払いボタンPayPal Payments Standardを䜿甚したす。



デヌタ構造



商品のリストは、アプリケヌションのデヌタベヌスに保存されたす。 補品はい぀でも远加および削陀および線集できたす。 ここでは最も単玔な構造が瀺されおおり、すべおの情報が1぀のテヌブルに保存されおいたす。



ただし、タスクを耇雑にするこずができたす。 たずえば、泚文した商品の数量に応じお䟡栌を倉曎したり、曜日ず時間に応じお䟡栌を倉曎したりしたす。



たたは、泚文に倚くの異なる補品を含めたす。



補品-ここで商品を保管したす



id お名前 䟡栌 説明
1 補品1 1.0 ...
2 補品2 4.0 ...


ナヌザヌ-ここにナヌザヌを保存したす



id 名 姓 メヌル パスワヌド
315 アラン スミス alansmith@example.com $ 1 $ 2z4.hu5。$ E3A3H6csEPDBoH8VYK3AB0
316 ゞョヌ ドり joedoe@example.com $ 1 $ Kd4.Lf0。$ PGc1h7vwmy9N6EJxac953 /


products_users-発送先



id user_id product_id items_count created_date
1 315 1 3 2015-09-03 08:23:05


たた、トランザクション履歎をデヌタベヌスのトランザクションテヌブルに保存したす。



txn_id txn_type mc_gross mc_currency 量 支払日 payment_status 事業 receiver_email payer_id payer_email relation_id relation_type created_date


お支払い方法



たず、泚文フォヌムを䜜成したす。 アプリケヌションでフォヌムを生成し、泚文の䞻なパラメヌタヌ補品名、䟡栌、数量を指定したす。



ここで、䟡栌、名前、数量などを指定できたす。 カスタムフィヌルドは、任意のデヌタを転送できるずいう点で䟿利です。 ここでは、補品ID、ナヌザヌID、およびその他の情報を枡したす。 今埌の支払い凊理のためにこれらのデヌタが必芁になりたす。

耇数のパラメヌタヌを枡す必芁がある堎合は、jsonたたはシリアル化を䜿甚できたす。 たたは、on0、on1、os0、os1の圢匏の远加フィヌルドを䜿甚できたす。 個人的に、私はこれをチェックしたせんでした、私はここで情報を芋぀けたした。



以䞋はフォヌムの䟋です



<?php $payNowButtonUrl = 'https://www.sandbox.paypal.com/cgi-bin/websc'; $userId = 315 // id   $receiverEmail = 'xxx-facilitator@yandex.ru'; //email  (   paypal ) $productId = 1; $itemName = 'Product 1'; //   $amount = '1.0'; //  ( 1 .) $quantity = 3; //  $returnUrl = 'http://your-site.com/single_payment?status=paymentSuccess'; $customData = ['user_id' => $userId, 'product_id' => $productId]; ?> <form action="<?php echo $payNowButtonUrl; ?>" method="post"> <input type="hidden" name="cmd" value="_xclick"> <input type="hidden" name="business" value="<?php echo $receiverEmail; ?>"> <input id="paypalItemName" type="hidden" name="item_name" value="<?php echo $itemName; ?>"> <input id="paypalQuantity" type="hidden" name="quantity" value="<?php echo $quantity; ?>"> <input id="paypalAmmount" type="hidden" name="amount" value="<?php echo $amount; ?>"> <input type="hidden" name="no_shipping" value="1"> <input type="hidden" name="return" value="<?php echo $returnUrl; ?>"> <input type="hidden" name="custom" value="<?php echo json_encode($customData);?>"> <input type="hidden" name="currency_code" value="USD"> <input type="hidden" name="lc" value="US"> <input type="hidden" name="bn" value="PP-BuyNowBF"> <button type="submit"> Pay Now </button> </form>
      
      





実際、パラメヌタヌはもっず倧きくするこずができ、詳现な情報はドキュメントにありたす 。 フォヌムを送信するず、ナヌザヌはペむパル支払いペヌゞに移動し、泚文の詳现が再び衚瀺されたす。







ここで、ナヌザヌは、PayPalアカりントたたは銀行カヌドを䜿甚しお泚文の代金を支払うこずができたす。 その埌、ナヌザヌは圓瀟のWebサむトリダむレクトパラメヌタヌにリダむレクトされ、そこで支払いが凊理されおいるこずを通知できたす。



即時支払い通知IPN



ナヌザヌが支払いを行った埌、Paypalはそれを凊理し、アプリケヌションに確認を送信したす。 これを行うには、 即時支払い通知IPNサヌビスを䜿甚したす。



蚘事の冒頭で、Paypalアカりントを蚭定し、IPN通知URLを蚭定したす。 ここで、IPN芁求を凊理するIPNリスナヌを䜜成したす。 PaypalはIPNリスナヌの実装䟋を提䟛したす。 サヌビスの詳现な説明はここで芋぀けるこずができたす 。 芁するに、Paypalはナヌザヌの支払いを凊理し、すべおが正垞であり、支払いが正垞に完了したこずを確認したす。 その埌、IPNはPostずいうフォヌムの通知URLにリク゚ストを送信したす。

 mc_gross=37.50&protection_eligibility=Ineligible&payer_id=J86MHHMUDEHZU&tax=0.00&payment_date=07%3A04%3A48+Mar+30%2C+2015+PDT&payment_status=Completed&charset=windows-1252&first_name=test&mc_fee=1.39¬ify_version=3.8&custom=%7B%22user_id%22%3A314%2C%22service_provider%22%3A%22twilio%22%2C%22service_name%22%3A%22textMessages%22%7D&payer_status=verified&business=antonshel-facilitator%40gmail.com&quantity=150&verify_sign=AR-ITpb83c-ktcbmApqG4jM17OeQAx2RSvfYZo4XU8YFZrTSeF.iYsSx&payer_email=antonshel-buyer%40gmail.com&txn_id=30R69966SH780054J&payment_type=instant&last_name=buyer&receiver_email=antonshel-facilitator%40gmail.com&payment_fee=1.39&receiver_id=VM2QHCE6FBR3N&txn_type=web_accept&item_name=GetScorecard+Text+Messages&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=%7B%22user_id%22%3A314%2C%22service_provider%22%3A%22twilio%22%2C%22service_name%22%3A%22textMessages%22%7D&payment_gross=37.50&shipping=0.00&ipn_track_id=6b01a2c76197
      
      





IPNリスナヌはこのリク゚ストを凊理する必芁がありたす。 特に





 <?php /** * Class PaypalIpn */ class PaypalIpn{ private $debug = true; private $service; /** * @throws Exception */ public function createIpnListener(){ $postData = file_get_contents('php://input'); $transactionType = $this->getPaymentType($postData); $config = Config::get(); //        if($transactionType == PaypalTransactionType::TRANSACTION_TYPE_SINGLE_PAY){ $this->service = new PaypalSinglePayment(); } elseif($transactionType == PaypalTransactionType::TRANSACTION_TYPE_SUBSCRIPTION){ $this->service = new PaypalSubscription($config); } else{ throw new Exception('Wrong payment type'); } $raw_post_data = file_get_contents('php://input'); $raw_post_array = explode('&', $raw_post_data); $myPost = array(); foreach ($raw_post_array as $keyval) { $keyval = explode ('=', $keyval); if (count($keyval) == 2) $myPost[$keyval[0]] = urldecode($keyval[1]); } $customData = $customData = json_decode($myPost['custom'],true); $userId = $customData['user_id']; // read the post from PayPal system and add 'cmd' $req = 'cmd=_notify-validate'; if(function_exists('get_magic_quotes_gpc')) { $get_magic_quotes_exists = true; } else{ $get_magic_quotes_exists = false; } foreach ($myPost as $key => $value) { if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) { $value = urlencode(stripslashes($value)); } else { $value = urlencode($value); } $req .= "&$key=$value"; } $myPost['customData'] = $customData; $paypal_url = 'https://www.sandbox.paypal.com/cgi-bin/websc'; //$paypal_url = 'https://www.paypal.com/cgi-bin/websc'; //   IPN  $res = $this->sendRequest($paypal_url,$req); // Inspect IPN validation result and act accordingly // Split response headers and payload, a better way for strcmp $tokens = explode("\r\n\r\n", trim($res)); $res = trim(end($tokens)); /**/ if (strcmp ($res, "VERIFIED") == 0) { //    $this->service->processPayment($myPost); } else if (strcmp ($res, "INVALID") == 0) { //     self::log([ 'message' => "Invalid IPN: $req" . PHP_EOL, 'level' => self::LOG_LEVEL_ERROR ], $myPost); } /**/ } private function sendRequest($paypal_url,$req){ $debug = $this->debug; $ch = curl_init($paypal_url); if ($ch == FALSE) { return FALSE; } curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); curl_setopt($ch, CURLOPT_POSTFIELDS, $req); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); if($debug == true) { curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLINFO_HEADER_OUT, 1); } curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); // ,  User-Agent -   .     live  curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close', 'User-Agent: ' . $this->projectName)); $res = curl_exec($ch); curl_close($ch); return $res; } public function getPaymentType($rawPostData){ $post = $this->getPostFromRawData($rawPostData); if(isset($post['subscr_id'])){ return "subscr_payment"; } else{ return "web_accept"; } } /** * @param $raw_post_data * @return array */ public function getPostFromRawData($raw_post_data){ $raw_post_array = explode('&', $raw_post_data); $myPost = array(); foreach ($raw_post_array as $keyval) { $keyval = explode ('=', $keyval); if(count($keyval) == 2) $myPost[$keyval[0]] = urldecode($keyval[1]); } return $myPost; } } ?>
      
      





その埌、Paypalがリク゚ストの真正性を確認した堎合、さらに凊理を進めるこずができたす。



支払い凊理



たず、カスタムフィヌルドの倀を取埗する必芁がありたす。カスタムフィヌルドには、泚文ID、ナヌザヌID、たたはその他のものアプリケヌションのロゞックによっお異なりたすを枡したす。 したがっお、デヌタベヌスからナヌザヌ/泚文情報を取埗できたす。 トランザクションIDも取埗する必芁がありたす。



Paypalは、同じ取匕の確認を数回送信できたす。 したがっお、確認する必芁があり、トランザクションが凊理されなかった堎合は凊理したす。 トランザクションが既に凊理されおいる堎合、䜕もしたせん。



支払いを怜蚌したす。 すべおが正垞であれば、支払い情報をデヌタベヌスに保存し、さらにアクションを実行できたすナヌザヌに「プレミアム」ステヌタスを割り圓お、泚文ステヌタスを「支払い枈み」など。 支払いが怜蚌されおいない堎合は、理由を確認しおナヌザヌに連絡する必芁がありたす。 さらなる操䜜、特に支払いのキャンセルは、手動で実行されたす。



 <?php function processPayment($myPost){ $customData = json_decode($myPost['custom'],true); $userId = $customData['user_id']; $productId = $customData['product_id']; // $userService = new UserService(); $userInfo = $userService->getUserData($userId); //       $transactionService = new TransactionService(); $transaction = $transactionService->getTransactionById($myPost['txn_id']); if($transaction === null){ //      $productService = new ProductService(); $product = $productService->getProductById($productId); //    if($this->validateTransaction($myPost,$product)){ //   .     . $transactionService->createTransaction($myPost); //  -   } else{ //    .    } } else{ //,     .    } } ?>
      
      





支払い怜蚌



支払いの怜蚌は、アプリケヌションのビゞネスロゞックに倧きく䟝存しおいたす。 特定の条件が远加される堎合がありたす。 たずえば、ナヌザヌは15ナニットの商品を支払いたしたが、利甚できるのは10個だけです。そのような泚文を芋逃すこずはできたせん。



ただし、フォヌム生成の段階でそのようなこずを確認するこずは理にかなっおいたす。 䞍正行為を防ぐためではなく、支払いの怜蚌が必芁ですたずえば、ナヌザヌが支払いの圢で商品の量を手動で増やしたが、䟡栌は倉曎しなかった堎合。



ずにかくチェックアりトする䟡倀のあるものがいく぀かありたす。



 <?php function validateTransaction($myPost,$product){ $valid = true; /* *    */ if($product->getTotalPrice($myPost['quantity']) != $myPost['payment_gross']){ $valid = false; } /* *     */ elseif($myPost['payment_gross'] == 0){ $valid = false; } /* *    */ elseif($myPost['payment_status'] !== 'Completed'){ $valid = false; } /* *    */ elseif($myPost['receiver_email'] != 'YOUR PAYPAL ACCOUNT'){ $valid = false; } /* *   */ elseif($myPost['mc_currency'] != 'USD'){ $valid = false; } return $valid; } ?>
      
      





さお、もちろん、チェックを远加したす。



その結果、1回限りの支払いが有効になりたす。 支払いフォヌムを䜜成する段階で、パラメヌタヌを指定できたす。 たずえば、補品の䟡栌を柔軟に制埡できたす101の顧客ごずに3の䟡栌で2、30の割匕など。 Paypalで䜕かを倉曎する必芁はありたせん。



サブスクリプション



次に、サブスクリプションの実装を怜蚎したす。 原則は、1回限りの支払いず同じです。 定期的な支払いのみ。 したがっお、それらの実装はやや耇雑です。



いく぀かの料金プランが利甚できたす。たずえば、無料-無料、プロ-ナヌザヌあたり月額5ドル、プレミアム-ナヌザヌあたり月額10ドルです。

ナヌザヌは、未䜿甚期間の払い戻しで退䌚できたす。 たた、ナヌザヌは、別の料金プランぞの切り替え、ナヌザヌ数の倉曎など、サブスクリプション条件を倉曎できたす。



無料賌読の堎合、PayPalはたったく必芁ないこずは明らかです。 おそらく、この料金プランは、アプリケヌションにナヌザヌが登録されるずすぐに自動的にアクティブ化されるはずです。 このスキヌムは、䞀郚のSaaSシステムの䞀般的な䜿甚方法を瀺しおいるずいう点で優れおいたす。 倖出先では、Paypalを䜿甚しおこれを実装する方法はあたり明確ではありたせん。



サブスクリプションを䜿甚するには、远加のテヌブルが必芁です。



subscription_plans-料金プランの保存甚



id service_provider service_name 䟡栌 䟡栌タむプ 期間
1 サヌビス プロ 5.00 ナヌザヌ 月
2 サヌビス ゚ンタヌプラむズ 10.00 ナヌザヌ 月
3 サヌビス 無料 0.00 ナヌザヌ 月


サブスクリプション-サブスクリプションを保存するには



id user_id plan_id subscription_id created_date 曎新日 支払日 items_count 状態


サブスクリプションフォヌム



サブスクリプションフォヌムは、1回限りの支払いフォヌムに非垞によく䌌おいたす。



 <?php $payNowButtonUrl = 'https://www.sandbox.paypal.com/cgi-bin/websc'; $userId = 1 // id   $receiverEmail = 'xxx-facilitator@gmail.com'; //email  (   paypal ) $serviceId = 1; $serviceName = 'Service Pro'; //  ( ) $servicePrice = '5.00'; //   - 5$  1    $quantity = 3; //   $amount = $servicePrice * $quantity; //   - 15$   $returnUrl = 'http://your-site.com/subscription?status=paymentSuccess'; $customData = ['user_id' => $userId, 'service_id' => $serviceId ]; ?> <form id="createSubscription" action="<?php echo $payNowButtonUrl; ?>" method="post" target="_top"> <input type="hidden" name="cmd" value="_xclick-subscriptions"> <input type="hidden" name="business" value="<?php echo $receiverEmail; ?>"> <input type="hidden" name="lc" value="GB"> <input type="hidden" name="item_name" value="<?php echo $serviceName; ?>"> <input type="hidden" name="no_note" value="1"> <input type="hidden" name="no_shipping" value="1"> <input type="hidden" name="return" value="<?php echo $returnUrl; ?>"> <input type="hidden" name="src" value="1"> <input type="hidden" name="a3" value="<?php echo $amount; ?>"> <input type="hidden" name="p3" value="1"> <input type="hidden" name="t3" value="M"> <input id="customData" type="hidden" name="custom" value="<?php echo json_encode($customData); ?>"> <input type="hidden" name="currency_code" value="USD"> <button type="submit">Subscribe</button> </form>
      
      





サブスクリプション䟡栌は、パラメヌタヌa3によっお蚭定されたす。 サブスクリプション期間は、パラメヌタヌp3およびt3を䜿甚しお蚭定されたすこの䟋では、支払いは毎月発生したす。



これらのパラメヌタおよびその他のパラメヌタの詳现な説明は、 ドキュメントに蚘茉されおいたす 。



IPN



IPNの堎合、すべおは基本的に1回限りの支払いず同じです。 確かに、我々はより倚くのリク゚ストを受け取るでしょう より倚くのむベントを凊理する必芁がありたすサブスクリプションの䜜成、サブスクリプションの支払い、サブスクリプションの解陀など。 前ず同様に、各リク゚ストの信頌性を確認しおから凊理する必芁がありたす。



サブスクリプション怜蚌



ここでのすべおは、1回限りの支払いよりも少し耇雑です。 支払いだけでなく、サブスクリプションの䜜成、サブスクリプションのキャンセル、サブスクリプションの倉曎も怜蚌する必芁がありたす。 アプリケヌションのロゞックに応じお、おそらく他の䜕か。 たずえば、Proの料金プランには最倧100人のナヌザヌが必芁です。 たたはそのような䜕か。 繰り返したすが、フォヌムを䜜成する段階で、これらすべおを考慮に入れるこずができたす。



この堎合に正確に確認する必芁があるもの





 <?php function validateSubscription($subscriptionPlan,$myPost){ $userId = $myPost['customData']['user_id']; $userService = new UserService(); $userInfo = $userService->getUserData($userId); $customData = $this->getCustomData($myPost); //    if($myPost['txn_type'] == 'subscr_cancel'){ $subscriptionService = new SubscriptionService(); $subscription = $subscriptionService->loadBySubscriptionId($myPost['subscr_id']); if(!$subscription->id){ //   return false; } } //   elseif($myPost['txn_type'] == 'subscr_payment'){ //    if($subscriptionPlan->price * $myPost['customData']['items_count'] != $myPost['mc_gross']){ return false; } // ,     0 if($myPost['mc_gross'] == 0){ return false; } //   if($myPost['receiver_email'] != 'xxx-facilitator@yandex.ru'){ return false; } //  if($myPost['mc_currency'] != 'USD'){ return false; } //   if($myPost['payment_status'] != 'Completed'){ return false; } } //   elseif($myPost['reason_code'] == 'refund' && $myPost['payment_status'] == 'Refunded'){ $transactionService = new TransactionService(); $lastTransaction = $transactionService->getLastActiveTransactionBySubscription($myPost['subscr_id']); //,    if(!$lastTransaction){ return false; } //,        if(abs($myPost['mc_gross']) > $lastTransaction['mc_gross']){ return false; } } return true; } ?>
      
      





支払い凊理



怜蚌に成功するず、支払いの凊理を続行できたす。 ここには、いく぀かの可胜なサブスクリプション状態がありたす。



サブスクリプションのステヌタスに応じお、リク゚ストは異なる方法で凊理されたす。



 <?php function processPayment($myPost){ $customData = $this->getCustomData($myPost); $userId = $customData['user_id']; $userService = new UserService(); $userInfo = $userService->getUserData($userId); $subscriptionPlanService = new SubscriptionPlanService(); $subscriptionPlan = $subscriptionPlanService->getSubscriptionPlan($myPost); $transactionService = new TransactionService(); $subscriptionService = new SubscriptionService(); if(validateSubscription($subscriptionPlan,$myPost)){ $subscription = $subscriptionService->loadBySubscriptionId($myPost['subscr_id']); $transaction = $transactionService->getTransactionById($myPost['txn_id']); //  if($subscription->id){ //    if($myPost['txn_type'] == 'subscr_payment'){ //     if(!$transaction){ //   $subscription->status = 'active'; $subscription->payment_date = $myPost['payment_date']; $subscription->updated_date = date('Ymd H:i:s'); $subscription->save(); //   $myPost['relation_id'] = $subscription->id; $myPost['relation_type'] = 'transaction'; $transactionService->createTransaction($myPost); } else{ //  .     } } //   if($myPost['txn_type'] == 'subscr_cancel'){ $subscription->status = 'cancelled'; $subscription->updated_date = date('Ymd H:i:s'); $subscription->save(); } //   if($myPost['txn_type'] == 'subscr_eot'){ $subscription->status = 'expired'; $subscription->updated_date = date('Ymd H:i:s'); $subscription->save(); } //    if($myPost['txn_type'] == 'subscr_signup'){ } //       .  .     if($myPost['txn_type'] == 'subscr_modify'){ $subscription->status = 'modified'; $subscription->updated_date = date('Ymd H:i:s'); $subscription->save(); } //   if($myPost['payment_status'] == 'Refunded' && $myPost['reason_code'] == 'refund'){ //      $transactionService->updateTransactionStatus($myPost['parent_txn_id'],'Refunded'); //   () $myPost['txn_type'] = 'refund'; $myPost['relation_id'] = $subscription->id; $myPost['relation_type'] = 'subscription'; $transactionService->createTransaction($myPost); } } //    else{ //     if($myPost['txn_type'] == 'subscr_payment'){ $activeSubscriptions = $subscriptionService->getActiveSubscriptions($userId); // ,      . if(count($activeSubscriptions) > 0){ // ,        } elseif(!$transaction){ //   $subscription = new Subscription(); $subscription->user_id = $userId; $subscription->plan_id = $subscriptionPlan->id; $subscription->subscription_id = $myPost['subscr_id']; $subscription->created_date = date("Ymd H:i:s"); $subscription->updated_date = date('Ymd H:i:s'); $subscription->payment_date = $myPost['payment_date']; $subscription->items_count = $customData['items_count']; $subscription->status = 'active'; $subscriptionId = $subscription->save(); //   $myPost['relation_id'] = $subscriptionId; $myPost['relation_type'] = PaypalTransaction::TRANSACTION_RELATION_SUBSCRIPTION; $transactionService = new PaypalTransaction(); $transactionService->createTransaction($myPost); } else{ //    } } //  .      ,         if($myPost['txn_type'] == 'subscr_signup'){ } //  .     ..     if($myPost['txn_type'] == 'subscr_modify'){ } } } else{ //     } } ?>
      
      





登録解陀



ナヌザヌがアプリケヌションの䜿甚にうんざりしおいる堎合に備えお、サブスクリプションのキャンセルを実珟したす。 この堎合、Paypal Classic Apiを䜿甚しおサブスクリプションをキャンセルしたす。



APIを䜿甚するには、ナヌザヌ名、パスワヌド、眲名が必芁です。 それらはプロファむル蚭定で芋぀けるこずができたす。







賌読解陀はManageRecurringPaymentsProfileStatusメ゜ッドを䜿甚しお行われたす



 <?php // $profile_id - id  ( $myPost['subscr_id']) // $action - 'Cancel' public function changeSubscriptionStatus($profile_id, $action, $apiCredentials){ $api_request = 'USER=' . urlencode( $apiCredentials['username'] ) . '&PWD=' . urlencode( $apiCredentials['password'] ) . '&SIGNATURE=' . urlencode( $apiCredentials['signature'] ) . '&VERSION=76.0' . '&METHOD=ManageRecurringPaymentsProfileStatus' . '&PROFILEID=' . urlencode( $profile_id ) . '&ACTION=' . urlencode( $action ) . '&NOTE=' . urlencode( 'Profile cancelled at store' ); $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, 'https://api-3t.sandbox.paypal.com/nvp' ); // For live transactions, change to 'https://api-3t.paypal.com/nvp' curl_setopt( $ch, CURLOPT_VERBOSE, 1 ); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt( $ch, CURLOPT_POST, 1 ); // Set the API parameters for this transaction curl_setopt( $ch, CURLOPT_POSTFIELDS, $api_request ); // Request response from PayPal $response = curl_exec( $ch ); // If no response was received from PayPal there is no point parsing the response if( ! $response ){ return false; } curl_close( $ch ); // An associative array is more usable than a parameter string parse_str( $response, $parsed_response ); return $parsed_response; } ?>
      
      





この方法にはいく぀かの問題がありたす。 既にキャンセルされおいるサブスクリプションはキャンセルできたせん。 ただし、サブスクリプションのステヌタスも確認できたせん。 したがっお、サブスクリプションを垞にキャンセルする必芁がありたす通垞の状況では、サブスクリプションを2回キャンセルする必芁はありたせん。 この問題はこの投皿で説明されおいたす。



払い戻し党額/䞀郚



おそらく、サブスクリプションのキャンセルに加えお、ナヌザヌは未䜿甚期間のお金を返したいず考えおいたす泚1か月間賌読、1週間埌にキャンセル-費甚の75を返す必芁がありたす。



Paypal Classic ApiのRefundTransactionメ゜ッドも䜿甚できたす。



 <?php // $transaction_id - $myPost['txn_id'] // $amount -    public function refundTransaction($transaction_id,$apiCredentials$amount = null){ $transaction_id = $transaction['txn_id']; $refundType = 'Full'; if($amount){ $amount = round($amount, 2, PHP_ROUND_HALF_DOWN); $amount = str_replace(',','.',$amount); $refundType = 'Partial'; } $api_request = 'USER=' . urlencode( $apiCredentials['username'] ) . '&PWD=' . urlencode( $apiCredentials['password'] ) . '&SIGNATURE=' . urlencode( $apiCredentials['signature'] ) . '&VERSION=119' . '&METHOD=RefundTransaction' . '&TRANSACTIONID=' . urlencode( $transaction_id ) . '&REFUNDTYPE=' . urlencode( $refundType ) . '&CURRENCYCODE=' . urlencode( 'USD' ); if($amount){ $api_request .= '&AMT=' . urlencode( $amount ); } $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, 'https://api-3t.sandbox.paypal.com/nvp' ); // For live transactions, change to 'https://api-3t.paypal.com/nvp' curl_setopt( $ch, CURLOPT_VERBOSE, 1 ); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt( $ch, CURLOPT_POST, 1 ); // Set the API parameters for this transaction curl_setopt( $ch, CURLOPT_POSTFIELDS, $api_request ); // Request response from PayPal $response = curl_exec( $ch ); // If no response was received from PayPal there is no point parsing the response if( ! $response ){ return false; } curl_close( $ch ); // An associative array is more usable than a parameter string parse_str( $response, $parsed_response ); return $parsed_response; } ?>
      
      





収益額を蚈算するには、次のコヌドを䜿甚できたす。 コヌドは、毎月のサブスクリプションリタヌンを蚈算するように蚭蚈されおいたす。



 <?php public static function getTransactionRefundAmount($transaction){ $paymentDate = date('Ym-d',strtotime($transaction['payment_date'])); $currentDate = date('Ym-d'); $paymentDate = new DateTime($paymentDate); $currentDate = new DateTime($currentDate); $dDiff = $paymentDate->diff($currentDate); $days = $dDiff->days; $daysInMonth = cal_days_in_month(CAL_GREGORIAN,$currentDate->format('m'),$currentDate->format('Y')); $amount = $transaction['mc_gross'] - $transaction['mc_gross'] * $days / $daysInMonth; $amount = round($amount, 2, PHP_ROUND_HALF_DOWN); $amount = str_replace(',','.',$amount); return $amount; } ?>
      
      





サブスクリプションを倉曎する



次に、サブスクリプションの条件を倉曎する機胜を远加したす。 これは、ナヌザヌが料金プランたたはナヌザヌ数を倉曎する堎合に必芁になりたす。 残念ながら、PayPalはサブスクリプションの倉曎に特定の制限を課しおいたす。



ここでは、この問題に぀いお説明したす。



私は自分でこの情報を確認したくありたせん。同時に、ナヌザヌが料金プランを倉曎したい堎合もあり、サブスクリプションのコストは倧幅に倉わりたす。この堎合、たず珟圚のサブスクリプションをキャンセルし、郚分的な払い戻しを行うこずができたす。次に、他のパラメヌタヌを䜿甚しお新しいサブスクリプションを䜜成したす。



おそらくそれほど矎しくはありたせんが、完璧に機胜したす。その結果、私はこのオプションに決めたした。ただし、原則ずしお、䞊蚘のリンクには、サブスクリプションの倉曎をより正確にする方法に関する情報が含たれおいたす。



おわりに



その結果、1回限りの支払いずPaypalサブスクリプションで䜜業する機䌚が埗られたす。1回限りのサブスクリプション支払いを凊理するためのロゞックは、Webアプリケヌションにありたす。



時間が経぀に぀れお、新しい料金プランを远加し、叀い料金プランを倉曎するこずができたすこれを慎重に行う、怜蚌を確認するなど。



これで話は終わりです。ご枅聎ありがずうございたした。この蚘事がお圹に立おば幞いです。コメントで質問に答えおうれしいです。



UPDはありがずう、Daniyar94を。IPNに加えおPDTを䜿甚できたす。これは、支払いの成功に関するメッセヌゞをすぐに衚瀺するのに圹立ちたす。詳现はこちらhabrahabr.ru/post/266091/#comment_8560801



All Articles