Source of file EcommercePaymentFormSetupAndValidation.php
Size: 15,408 Bytes - Last Modified: 2021-12-23T10:39:35+00:00
/var/www/docs.ssmods.com/process/src/src/Forms/Validation/EcommercePaymentFormSetupAndValidation.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 | <?php namespace Sunnysideup\Ecommerce\Forms\Validation; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Extensible; use SilverStripe\Core\Injector\Injectable; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\Form; use SilverStripe\Forms\TextField; use Sunnysideup\Ecommerce\Config\EcommerceConfigClassNames; use Sunnysideup\Ecommerce\Forms\Fields\EcommerceCreditCardField; use Sunnysideup\Ecommerce\Forms\Fields\ExpiryDateField; use Sunnysideup\Ecommerce\Model\Money\EcommercePayment; use Sunnysideup\Ecommerce\Model\Order; use Sunnysideup\Ecommerce\Money\Payment\EcommercePaymentResult; //get_credit_card_payment_form_fields: $formHelper->getCreditCardPaymentFormFields //get_credit_card_payment_form_fields_required: : $formHelper->getCreditCardPaymentFormFieldsRequired //validate_payment: $formHelper->validatePayment //validate_and_save_credit_card_information: validateAndSaveCreditCardInformation //process_payment_form_and_return_next_step: processPaymentFormAndReturnNextStep //validate_card_number: validateCardNumber //validate_expiry_month: :validateExpiryMonth //validate_CVV: validateCVV class EcommercePaymentFormSetupAndValidation { use Configurable; use Extensible; use Injectable; /** * @var EcommercePayment */ protected $paymentObject; /** * you can set specific EcommercePayment payment fields here, like this: * MyEcommercePaymentClass * CardNumber: MyCardNumberDBField * NameOnCard: MyNameOnCardDBField * CVVNumber: MyCVVNumberDBField * ExpiryDate: MyExpiryDateDBField. * * @var string */ private static $table_name = 'EcommercePaymentFormSetupAndValidation'; private static $db_field_map = []; /** * Return the payment form fields that should * be shown on the checkout order form for the * payment type. Example: for {@link DPSPayment}, * this would be a set of fields to enter your * credit card details. * * @param EcommercePayment $paymentObject * * @return \SilverStripe\Forms\FieldList */ public function getCreditCardPaymentFormFields($paymentObject = null) { if ($paymentObject) { $this->paymentObject = $paymentObject; } $paymentClassName = $this->paymentObject->ClassName; $fieldList = new FieldList( [ $CardNumberField = new EcommerceCreditCardField( $paymentClassName . '_CardNumber', _t('EcommercePaymentFormSetupAndValidation.CardNumber', 'Card Number') ), $nameOnCardField = new TextField( $paymentClassName . '_NameOnCard', _t('EcommercePaymentFormSetupAndValidation.NAMEONCARD', 'Name on Card') ), $expiryDateField = new ExpiryDateField( $paymentClassName . '_ExpiryDate', _t('EcommercePaymentFormSetupAndValidation.EXPIRYDATE', 'Expiry Date') ), $cvvNumberField = new TextField( $paymentClassName . '_CVVNumber', _t('EcommercePaymentFormSetupAndValidation.CVVNumber', 'Security Number') ), ] ); $nameOnCardField->setAttribute('maxlength', '40'); $cvvNumberField->setAttribute('maxlength', '4'); $cvvNumberField->setAttribute('size', '4'); $cvvNumberField->setAttribute('autocomplete', 'off'); return $fieldList; } /* * * @param EcommercePayment $paymentObject * * @return array */ public function getCreditCardPaymentFormFieldsRequired($paymentObject = null) { if ($paymentObject) { $this->paymentObject = $paymentObject; } $paymentClassName = $this->paymentObject->ClassName; return [ $paymentClassName . '_CardNumber', $paymentClassName . '_NameOnCard', $paymentClassName . '_ExpiryDate', $paymentClassName . '_CVVNumber', ]; } /** * @param Order $order - the order that is being paid * @param array $data - Array of data that is submittted * @param Form $form - the form that is being submitted * * @return bool - true if the data is valid */ public function validatePayment($order, $data, $form) { if (! $order) { $form->sessionMessage(_t('EcommercePayment.NOORDER', 'Order not found.'), 'bad'); return false; } //nothing to pay, always valid if ((0 === $order->TotalOutstanding() && $order->IsSubmitted()) || $order->IsPaid() || 0 === $order->Total()) { return true; } if (! $this->paymentObject) { $paymentClass = empty($data['PaymentMethod']) ? null : $data['PaymentMethod']; if ($paymentClass) { //important! convert back to PHP class $paymentClass = EcommercePayment::html_class_to_php_class($paymentClass); if (class_exists($paymentClass)) { $this->paymentObject = $paymentClass::create(); } } } if (! $this->paymentObject || ! ($this->paymentObject instanceof EcommercePayment)) { $form->sessionMessage(_t('EcommercePaymentFormSetupAndValidation.NOPAYMENTOPTION', 'No Payment option selected.'), 'bad'); return false; } // Check payment, get the result back return $this->paymentObject->validatePayment($data, $form); } /** * return false if there is an error and * returns true if all is well. * If there are no errors, then the payment object will also be written... * If there are errors, the errors will be added to the form. * * @param Form $form * @param array $data * @param EcommercePayment $paymentObject * * @return bool */ public function validateAndSaveCreditCardInformation($data, $form, $paymentObject = null) { $errors = false; if ($paymentObject) { $this->paymentObject = $paymentObject; } $paymentClassName = $this->paymentObject->ClassName; $dbFieldMap = Config::inst()->get(EcommercePaymentFormSetupAndValidation::class, 'db_field_map'); $cardNumberFormFields = [ 'CardNumber', 'ExpiryDate', 'CVVNumber', 'NameOnCard', ]; foreach ($cardNumberFormFields as $dbFieldName) { $formFieldName = $paymentClassName . '_' . $dbFieldName; //check if there is a credit card at all: if (! isset($data[$formFieldName])) { return true; } switch ($dbFieldName) { case 'CardNumber': if (isset($dbFieldMap[$paymentClassName]['CardNumber'])) { $dbFieldName = $dbFieldMap[$paymentClassName]['CardNumber']; } $this->paymentObject->{$dbFieldName} = trim( $data[$formFieldName][0] . $data[$formFieldName][1] . $data[$formFieldName][2] . $data[$formFieldName][3] ); $cardNumber = $this->paymentObject->{$dbFieldName}; if (! $this->validateCardNumber($this->paymentObject->{$dbFieldName})) { $form->sessionError( $formFieldName, _t('EcommercePaymentFormSetupAndValidation.INVALID_CREDIT_CARD', 'Invalid credit card number.'), 'bad' ); $errors = true; } break; case 'ExpiryDate': $this->paymentObject->{$dbFieldName} = $data[$formFieldName]['month'] . $data[$formFieldName]['year']; if (! $this->validateExpiryMonth($this->paymentObject->{$dbFieldName})) { $form->sessionError( $formFieldName, _t('EcommercePaymentFormSetupAndValidation.INVALID_EXPIRY_DATE', 'Expiry date not valid.'), 'bad' ); $errors = true; } break; case 'CVVNumber': $cardNumber = $this->paymentObject->{$dbFieldName}; $this->paymentObject->{$dbFieldName} = trim($data[$formFieldName]); if (! $this->validateCVV($cardNumber, $this->paymentObject->{$dbFieldName})) { $form->sessionError( $formFieldName, _t('EcommercePaymentFormSetupAndValidation.INVALID_CVV_NUMBER', 'Invalid security number.'), 'bad' ); } break; case 'NameOnCard': $this->paymentObject->{$dbFieldName} = trim($data[$formFieldName]); if (strlen($this->paymentObject->{$dbFieldName}) < 3) { $form->sessionError( $formFieldName, _t('EcommercePaymentFormSetupAndValidation.NO_CARD_NAME', 'No card name provided.'), 'bad' ); $errors = true; } break; default: user_error('Type must be one of four options: CardNumber, NameOnCard, CVV, ExpiryDate'); } } if ($errors) { $form->sessionMessage(_t('EcommercePaymentFormSetupAndValidation.PLEASE_REVIEW_CARD_DETAILS', 'Please review your card details.'), 'bad'); return false; } $this->paymentObject->write(); return true; } /** * Process payment form and return next step in the payment process. * Steps taken are: * 1. create new payment * 2. save form into payment * 3. return payment result. * * @param Order $order - the order that is being paid * @param array $data - Array of data that is submittted * @param Form $form - the form that is being submitted * * * @return bool - if successful, this method will return TRUE */ public function processPaymentFormAndReturnNextStep(Order $order, $data, Form $form) { if (! $this->paymentObject) { $paymentClass = empty($data['PaymentMethod']) ? null : $data['PaymentMethod']; if ($paymentClass) { if (class_exists($paymentClass)) { $this->paymentObject = $paymentClass::create(); } } } if (! $this->paymentObject) { return false; } // Save payment data from form and process payment $form->saveInto($this->paymentObject); $this->paymentObject->OrderID = $order->ID; //important to set the amount and currency (WE SET THEM BOTH AT THE SAME TIME!) $this->paymentObject->Amount = $order->getTotalOutstandingAsMoney(); $this->paymentObject->write(); // Process payment, get the result back $result = $this->paymentObject->processPayment($data, $form); if (! is_a($result, EcommerceConfigClassNames::getName(EcommercePaymentResult::class))) { $form->getController()->redirectBack(); return false; } if ($result->isProcessing()) { //IMPORTANT!!! // isProcessing(): Long payment process redirected to another website (PayPal, Worldpay) //redirection is taken care of by payment processor return $result->getValue(); } //payment is done, redirect to either returntolink //OR to the link of the order .... if (isset($data['returntolink'])) { $form->getController()->redirect($data['returntolink']); } else { $form->getController()->redirect($order->Link()); } return true; } /** * checks if a credit card is a real credit card number. * * @reference: http://en.wikipedia.org/wiki/Luhn_algorithm * * @param int|string $cardNumber * * @return bool */ public function validateCardNumber($cardNumber) { if (! $cardNumber) { return false; } for ($sum = 0, $i = strlen($cardNumber) - 1; $i >= 0; --$i) { $digit = (int) $cardNumber[$i]; $sum += ($i % 2) === 0 ? array_sum(str_split($digit * 2)) : $digit; } return ($sum % 10) === 0; } /** * @todo: finish! * valid expiry date * * @param string $monthYear - e.g. 0218 * * @return bool */ public function validateExpiryMonth($monthYear) { $month = (int) substr($monthYear, 0, 2); $year = (int) ('20' . substr($monthYear, 2)); $currentYear = (int) date('Y'); $currentMonth = (int) date('m'); if (($month > 0 || $month < 13) && $year > 0) { if ($year > $currentYear) { return true; } if ($year === $currentYear) { if ($currentMonth <= $month) { return true; } } } return false; } /** * @todo: TEST * valid CVC/CVV number? * * @param int $cardNumber * @param int $cvv * * @return bool */ public function validateCVV($cardNumber, $cvv) { $cardNumber = preg_replace('#\D#', '', $cardNumber); $cvv = preg_replace('#\D#', '', $cvv); //Checks to see whether the submitted value is numeric (After spaces and hyphens have been removed). if (is_numeric($cardNumber)) { //Checks to see whether the submitted value is numeric (After spaces and hyphens have been removed). if (is_numeric($cvv)) { //Splits up the card number into various identifying lengths. // $firstOne = substr($cardNumber, 0, 1); $firstTwo = substr($cardNumber, 0, 2); //If the card is an American Express if ('34' === $firstTwo || '37' === $firstTwo) { if (! preg_match('#^\\d{4}$#', $cvv)) { // The credit card is an American Express card // but does not have a four digit CVV code return false; } } elseif (! preg_match('#^\\d{3}$#', $cvv)) { // The credit card is a Visa, MasterCard, or Discover Card card // but does not have a three digit CVV code return false; } //passed all checks return true; } return false; } return false; } } |