Friday, June 1, 2018

How the flow of this context should be? ( Where to put the code to process the payment depending on the selected payment method?)

Leave a Comment

I have a multi-step form for a user to register in a conference, all steps are in the same registration.blade.php page, in step 1 and step 2 is done an ajax request to validate the form fields.

The steps are:

  • Step1 is to collect some info about the user (name and tin number)
  • Step 2 is to collect the payment method (credit card or references)
  • Step 3 is the payment
  • Step 4 shows a success message (this step only appears after a successful payment with a credit card, with the references payment method this step don't appear, with references payment method only the step 1, 2 and 3 appears)

My doubt is between the step 2 and 3.

If the selected payment method references is necessary to execute the code below, that generates some payment references and then present that references to the user in step 3. When the user pays the system receives a notification and should be inserted in the payments table the price, payment_method_id, registration_id and status (paid).

Code to process payments with references:

    public function ReferencesCharge(Request $request)         {            $payment_info = [         'name' => "user name",         'email' => 'user email',         'value' => 'registration total price',         'id' => 'registration id',     ];      $newPayment = new Payment($payment_info);     $reference = $newPayment->gererateReferences();      //$reference returns an array with necessary codes to present to the user so he can pay      // after generate references is necessary:             // show in step 3 the generated references             // insert an entry in the payments table when the system receives a notification from 3rd party service informing that the user did the payment  } 

If the selected payment was credit card is necessary to execute the code below to charge the credit card and after that is inserted in the payments table the price, payment_method_id, registration_id and status (paid). And then the user should be redirected to step 4 to show a success message:

Code to process credit card payments:

public function creditCardCharge(Request $request)     {         Stripe::setApiKey(config('services.stripe.secret'));         $source = $request->stripeToken;          try{             Charge::create([                 'currency' => 'eur',                 'amount' => 2500,                 'source' => $source,             ]);         }         catch(\Exception $e){             return response()->json(['status' => $e->getMessage()], 422);         }          // after charging the card with success:             // insert an entry in the payments table              // redirect to success confirmation step to inform the user that payment was done with success      } 

My doubt is how the flow should be, where the code to handle the payment with references and the code to handle the payment with a credit card should be placed. So is possible to achieve this scenario:

enter image description here

For now, I just have the step 1 and step 2 working properly. To handle step1 and step2 I have the RegistrationController. In the step1 the info introduced by the user is validated using an ajax post request to the storeUserInfo() method of the RegistrationController if returns 200 the user goes to step2.

In the step2, the user selects the payment method and clicks in "go to step 3" is also done an ajax request to the storePaymentMethods() of the RegistrationController to validate if the user selected at least 1 payment method. My doubt is after this method return code 200 how the process should be.

Depending on the payment method, it necessary to run the appropriate code above (code that generates payment references or code to charge credit card).

So, my doubt how to organize this code in terms of controllers and methods, where to put that code that should execute depending on the selected payment method. Do you know how the flow should be to achieve that?

Maybe an approach can be something like below in storePaymentMethods() but it doesn't seem very correct do everything in this method:

public function storePaymentMethods(Request $request){        $request->validate([             'payment_method' => 'required',         ]);          if($request->payment_method == "references"){           // generate codes and present to the user that codes         }         else if($request->payment_method == "credit_card"){           // show credit card inputs to the user           // and process the credit card payment with stripe         }            return response()->json([             'success' => true,             'message' => 'success',             'payment_method' => $request->payment_method,         ], 200);     } 

A full summary of the flow of the registration with the multistep form that I have for now:

So for the step1, there is the form:

<div>     <form method="post" id="step1form" action="">         {{csrf_field()}}         <!-- fields of the step 1-->         <input type="submit" href="#step2" id="goToStep2" class="btn next-step" value="Go to step 2"/>     </form> </div> 

step1 image to explain better:

enter image description here

when the user clicks in "Go to step 2" button is made an ajax request to validate the data and if there are no errors, code 200 is returned and the user go to the step 2:

$('#goToStep2').on('click', function (event) {     event.preventDefault();     var custom_form = $("#" + page_form_id_step1);     $.ajax({         method: "POST",         url: '{{ route('conferences.storeRegistrationInfo', compact('id','slug') ) }}',         data: custom_form.serialize(),         datatype: 'json',         success: function (data, textStatus, jqXHR) {             var $active = $('.nav-pills li a.active');             nextTab($active);         },         error: function (data) {                 // show errors         }     }); }); 

Then in the ConferencesController there is teh storeRegistrationInfo() to handle the above ajax request:

public function storeRegistrationInfo(Request $request, $id, $slug = null, Validator $validator){       $rules = [];     $messages = [];      $rules["name_invoice"] = 'required|max:255|string';     $rules["TIN_invoice"] = ['required', 'string', new ValidTIN()];      $validator = Validator::make($request->all(), $rules, $messages);      $errors = $validator->errors();     $errors =  json_decode($errors);      if($validator->fails()) {         return response()->json([             'success' => false,             'errors' => $errors         ], 422);     }     return response()->json([         'success' => true,         'message' => 'success'     ], 200); } 

So, if code 200 is returned user is in the step2form:

<div>     <form method="post" id="step2form" action="">         {{csrf_field()}}         <!-- fields of the step 2-->          <input type="submit" href="#step3" id="goToStep3" class="btn next-step" value="Go to step 3"/>     </form> </div> 

step2 image to explain better:

enter image description here

When the user clicks in "Go to step 3" button is made an ajax request to validate the data and if there are no errors, code 200 is returned and the user go to the step 3, ajax request:

$("#credit_card_section").hide(); $("#references_section").hide();  var page_form_id_step2 = "step2form";      $('#goToStep3').on('click', function (event) {         event.preventDefault();         var custom_form = $("#" + page_form_id_step2);         $.ajax({             method: "POST",             url: '{{ route('conferences.storePaymentMethods', compact('id','slug') ) }}',             data: custom_form.serialize(),             datatype: 'json',             success: function (data, textStatus, jqXHR) {                 var result = data;                 if(result['payment_method'] == 'credit_card'){                     $("#credit_card_section").show();                     $("#references_section").hide();                 }else{                     $("#references_section").show();                     $("#credit_card_section").hide();                 }                 var $active = $('.nav-pills li a.active');                 nextTab($active);             },             error: function (data) {                // show errors             }         });     }); 

The ConferenceController has the storePayment() to handle the ajax request above, it validates if the user selected a payment method and if yes returns code 200:

public function storePaymentMethods(Request $request){        $request->validate([             'payment_method' => 'required',         ]);         return response()->json([             'success' => true,             'message' => 'success',             'payment_method' => $request->payment_method,         ], 200);     } 

Then there is the step3 div. In the step3 div it will appear the div #credit_card_section visible or #references_section visible depending on the payment method selected in the previous step (credit card or references):

<div>     <form method="post" id="step3form" action="">             {{csrf_field()}}             <div id="credit_card_section">                 <!-- present necessary fields to                 payments with credit card-->             </div>               <div id="references_section">         <!-- present generated reference to the user so he can pay-->             </div>     </form> </div> 

step3 image to explain better, depending on the payment method the info that is necessary to show in step 3 is different. If the payment method was credit card also appears a step4 div showing a success message after a successful charge with a credit card:

enter image description here

Then there is the step 4 div that should show the success message, after a completed payment with a credit card:

<div id="step4">    <p>    <i class="fa fa-plus" aria-hidden="true"></i>    Payment and registration completed with  success.    </p> </div> 

// Resume of the methods of the RegistrationController that I have for now, the RegistrationController is the controller that I have that handles the multistep form

class RegistrationController extends Controller {     public :function storeQuantities(Request $request, $id, $slug = null){         // method that stores in session the ticket types         // selected by the user in the conference details page         Session::put('selectedRtypes', $selectedRtypes);         Session::put('allParticipants' , $allParticipants);         Session::put('customQuestions' ,  $selectedRtypes[$rtype->name]['questions']);          // and then redirects the user to the registartion page registration.blade.php          // using the route 'conferences.registration         // this route is associated with the displayRegistrationPage() method         return redirect(route('conferences.registration',['id' => $id, 'slug' => $slug]));     }     // method of the route 'conferences.registration' that displays  //  the registration.blade.php that is the page with the multi step form     public function displayRegistrationPage(Request $request, $id, $slug=null){         // get the session values         $selectedRtypes =  Session::get('selectedRtypes');         $allParticipants = Session::get('allParticipants');         $customQuestions = Session::get('customQuestions');     // redirect the user to the registration.blade.php         if(isset($selectedRtypes)) {             return view('conferences.registration',                 ['selectedRtypes' => $selectedRtypes, 'customQuestions' => $customQuestions, 'id' => $id, 'slug' => $slug]);         }         else{             // return user to the conference details page             return redirect(route('conferences.show',['id' => $id, 'slug' => $slug]));         }     }     /* the following methods are to handle the registration       multi step  form of the registration.blade.php view */     // method to handle the step1 of the multi step form     public function storeRegistrationInfo(Request $request, $id, $slug = null, Validator $validator){          // get and validate the fields of the step1         // and returns code 200 if al is ok          return response()->json([             'success' => true,             'message' => 'success'         ], 200);     }      // method to handle the step2 of the multi step form     public function storePaymentMethods(Request $request){          // validate if the payment_method field was filled         // if was filled return code 200          // and returns the payment_method          //so in the step 3 div is possible to show a section for        // when the payment_method is credit card (#credit_card_section)            or transfers ("transfers_section")         return response()->json([             'success' => true,             'message' => 'success',             'payment_method' => $request->payment_method,         ], 200);     } } 

Route for step1:

Route::post('/conference/{id}/{slug?}/registration/storeRegistrationInfo', [     'uses' => 'RegistrationController@storeRegistrationInfo',     'as'   =>'conferences.storeRegistrationInfo' ]); 

Route for step2:

Route::post('/conference/{id}/{slug?}/registration/storePaymentMethods', [     'uses' => 'RegistrationController@storePaymentMethods',     'as'   =>'conferences.storePaymentMethods' ]); 

2 Answers

Answers 1

As in your doubts, to better structure your code and separate logic (DRY concept), you should do few things:

  • Create PaymentsController with 3 functions (for the start, you will definitely add more here)

    1. for processing your references
    2. for processing credit cards
    3. for storing payment methods (store payment method in session, until user completes step3)

    You are totally free, on how you name these functions, but I would suggest something like processViaReferences, processViaCreditCard and storePaymentMethod.

    It will be much easier to read and validate, if you have them separated.

    Create corresponding post routes for them and reference appropriately, in cour code.

  • Create part views containing your forms only.

    Then include, these part views via @include to have it separated on view level. For your references in javascript/jQuery, remember to keep correct ids of them. You can extract javascript, to other view part views or separate js files also, if you have not done it already.

    Your view/form code should go to these part views...

Answers 2

There are no clear instructions, you need to decide for yourself how to organize your code.

If you can not choose exactly where to place the PHP code responsible for payment, then I would place processing of both payments in PaymentsController.php

So it is obvious that after choosing the method of payment you need to form something like an order in the database.

  1. In case of references they will have to be tied to this order.
  2. In case of using credit cards, payment will be tied to this order.
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment