require 'net/http' module ActiveMerchant #:nodoc: module Billing #:nodoc: module Integrations #:nodoc: module SagePayForm class Notification < ActiveMerchant::Billing::Integrations::Notification class CryptError < StandardError; end include Encryption def initialize(post_data, options) super load_crypt_params(params['crypt'], options[:credential2]) end # Was the transaction complete? def complete? status_code == 'OK' end # Text version of #complete?, since we don't support Pending. def status complete? ? 'Completed' : 'Failed' end # Status of transaction. List of possible values: # OK:: Transaction completed successfully. # NOTAUTHED:: Incorrect card details / insufficient funds. # MALFORMED:: Invalid input data. # INVALID:: Valid input data, but some fields are incorrect. # ABORT:: User hit cancel button or went idle for 15+ minutes. # REJECTED:: Rejected by account fraud screening rules. # AUTHENTICATED:: Authenticated card details secured at SagePay. # REGISTERED:: Non-authenticated card details secured at SagePay. # ERROR:: Problem internal to SagePay. def status_code params['Status'] end # Check this if #completed? is false. def message params['StatusDetail'] end # Vendor-supplied code (:order mapping). def item_id params['VendorTxCode'] end # Internal SagePay code, typically "{LONG-UUID}". def transaction_id params['VPSTxId'] end # Authorization number (only if #completed?). def auth_id params['TxAuthNo'] end # Total amount (no fees). def gross params['Amount'] end # AVS and CV2 check results. Possible values: # ALL MATCH:: # SECURITY CODE MATCH ONLY:: # ADDRESS MATCH ONLY:: # NO DATA MATCHES:: # DATA NOT CHECKED:: def avs_cv2_result params['AVSCV2'] end # Numeric address check. Possible values: # NOTPROVIDED:: # NOTCHECKED:: # MATCHED:: # NOTMATCHED:: def address_result params['AddressResult'] end # Post code check. Possible values: # NOTPROVIDED:: # NOTCHECKED:: # MATCHED:: # NOTMATCHED:: def post_code_result params['PostCodeResult'] end # CV2 code check. Possible values: # NOTPROVIDED:: # NOTCHECKED:: # MATCHED:: # NOTMATCHED:: def cv2_result params['CV2Result'] end # Was the Gift Aid box checked? def gift_aid? params['GiftAid'] == '1' end # Result of 3D Secure checks. Possible values: # OK:: Authenticated correctly. # NOTCHECKED:: Authentication not performed. # NOTAVAILABLE:: Card not auth-capable, or auth is otherwise impossible. # NOTAUTHED:: User failed authentication. # INCOMPLETE:: Authentication unable to complete. # ERROR:: Unable to attempt authentication due to data / service errors. def buyer_auth_result params['3DSecureStatus'] end # Encoded 3D Secure result code. def buyer_auth_result_code params['CAVV'] end # Address confirmation status. PayPal only. Possible values: # NONE:: # CONFIRMED:: # UNCONFIRMED:: def address_status params['AddressStatus'] end # Payer verification. Undocumented. def payer_verified? params['PayerStatus'] == 'VERIFIED' end # Credit card type. Possible values: # VISA:: Visa # MC:: MasterCard # DELTA:: Delta # SOLO:: Solo # MAESTRO:: Maestro (UK and International) # UKE:: Visa Electron # AMEX:: American Express # DC:: Diners Club # JCB:: JCB # LASER:: Laser # PAYPAL:: PayPal def credit_card_type params['CardType'] end # Last four digits of credit card. def credit_card_last_4_digits params['Last4Digits'] end # Used by composition methods, but not supplied by SagePay. def currency nil end def test? false end def acknowledge true end private def load_crypt_params(crypt, key) raise MissingCryptData if crypt.blank? raise MissingCryptKey if key.blank? crypt_data = sage_decrypt(crypt.gsub(' ', '+'), key) raise InvalidCryptData unless crypt_data =~ /(^|&)Status=/ params.clear parse(crypt_data) end class MissingCryptKey < CryptError def message 'No merchant decryption key supplied' end end class MissingCryptData < CryptError def message 'No data received from SagePay' end end class InvalidCryptData < CryptError def message 'Invalid data received from SagePay' end end end end end end end