def success(request): """ The order has been succesfully processed. """ session = SessionStore(session_key=request.POST.get("M_session")) transaction_id = request.POST.get("cartId") amount = request.POST.get("authAmount") request.session = session if request.POST.get("transStatus", "N") == "Y": order = Order.objects.get(pk=transaction_id) order.add_status(status="Processing", notes=_("Paid through WorldPay.")) request.user = order.contact.user record_payment(order, payment_module, amount=amount, transaction_id=transaction_id) for cart in Cart.objects.filter(customer=order.contact): cart.empty() return generic_success(request, template="checkout/worldpay/success.html") else: context = {"message": _("Your transaction was rejected.")} return render(request, "shop_404.html", context)
def process(self): # Execute the post to protx VSP DIRECT if self.valid: if self.settings.SKIP_POST.value: log.info("TESTING MODE - Skipping post to server. Would have posted %s?%s", self.url, self.postString) record_payment(self.order, self.settings, amount=self.order.balance, transaction_id="TESTING") return (True, 'OK', "TESTING MODE") else: self.log_extra("About to post to server: %s?%s", self.url, self.postString) conn = urllib2.Request(self.url, data=self.postString) try: f = urllib2.urlopen(conn) result = f.read() self.log_extra('Process: url=%s\nPacket=%s\nResult=%s', self.url, self.packet, result) except urllib2.URLError, ue: log.error("error opening %s\n%s", self.url, ue) print ue return (False, 'ERROR', 'Could not talk to Protx gateway') try: self.response = dict([row.split('=', 1) for row in result.splitlines()]) status = self.response['Status'] success = (status == 'OK') detail = self.response['StatusDetail'] if success: log.info('Success on order #%i, recording payment', self.order.id) record_payment(self.order, self.settings, amount=self.order.balance) #, transaction_id=transaction_id) return (success, status, detail) except Exception, e: log.info('Error submitting payment: %s', e) return (False, 'ERROR', 'Invalid response from payment gateway')
def one_step(request): # Check that items are in stock cart = Cart.objects.from_request(request) if cart.not_enough_stock(): return HttpResponseRedirect(reverse("satchmo_cart")) payment_module = config_get_group("PAYMENT_AUTOSUCCESS") # First verify that the customer exists try: contact = Contact.objects.from_request(request, create=False) except Contact.DoesNotExist: url = lookup_url(payment_module, "satchmo_checkout-step1") return HttpResponseRedirect(url) # Verify we still have items in the cart if cart.numItems == 0: template = lookup_template(payment_module, "checkout/empty_cart.html") return render(request, template) # Create a new order newOrder = Order(contact=contact) pay_ship_save(newOrder, cart, contact, shipping="", discount="") request.session["orderID"] = newOrder.id newOrder.add_status(status="Pending", notes="Order successfully submitted") record_payment(newOrder, payment_module, amount=newOrder.balance) success = lookup_url(payment_module, "satchmo_checkout-success") return HttpResponseRedirect(success)
def success(request): """ The order has been succesfully processed. """ session = SessionStore(session_key=request.POST.get('M_session')) transaction_id = request.POST.get('cartId') amount = request.POST.get('authAmount') request.session = session if request.POST.get('transStatus', 'N') == 'Y': order = Order.objects.get(pk=transaction_id) order.add_status(status='Processing', notes=_("Paid through WorldPay.")) request.user = order.contact.user record_payment(order, payment_module, amount=amount, transaction_id=transaction_id) for cart in Cart.objects.filter(customer=order.contact): cart.empty() return generic_success(request, template='checkout/worldpay/success.html') else: context = RequestContext( request, {'message': _('Your transaction was rejected.')}) return render_to_response('shop_404.html', context)
def one_step(request): payment_module = config_get_group('PAYMENT_AUTOSUCCESS') #First verify that the customer exists try: contact = Contact.objects.from_request(request, create=False) except Contact.DoesNotExist: url = lookup_url(payment_module, 'satchmo_checkout-step1') return HttpResponseRedirect(url) #Verify we still have items in the cart tempCart = Cart.objects.from_request(request) if tempCart.numItems == 0: template = lookup_template(payment_module, 'checkout/empty_cart.html') return render_to_response(template, RequestContext(request)) # Create a new order newOrder = Order(contact=contact) pay_ship_save(newOrder, tempCart, contact, shipping="", discount="") request.session['orderID'] = newOrder.id newOrder.add_status(status='Pending', notes = "Order successfully submitted") record_payment(newOrder, payment_module, amount=newOrder.balance) tempCart.empty() success = lookup_url(payment_module, 'satchmo_checkout-success') return HttpResponseRedirect(success)
def process(self): """ Purchase Orders are always successful. """ reason_code = "0" response_text = _("Success") record_payment(self.order, self.settings, amount=self.order.balance) return (True, reason_code, response_text)
def process_recurring_subscriptions(self, recurlist, testing=False): """Post all subscription requests.""" for recur in recurlist: success, reason, response, subscription_id = self.process_recurring_subscription(recur, testing=testing) if success: if not testing: record_payment(self.order, self.settings, amount=recur['charged_today'], transaction_id=subscription_id) else: self.log.info("Failed to process recurring subscription, %s: %s", reason, response) break return success, reason, response
def process(self): # process the transaction through tclink result = tclink.send(self.transactionData) status = result ['status'] if status == 'approved': record_payment(self.order, self.settings, amount=self.order.balance) return (True, status, result) if status == 'decline': msg = _(u'Transaction was declined. Reason: %s' % result['declinetype']) return (False, status, msg) if status == 'baddata': msg = _(u'Improperly formatted data. Offending fields: %s' % result['offenders']) return (False, status, msg) else: msg = _(u'An error occurred: %s' % result['errortype']) return (False, status, msg)
def process_recurring_subscriptions(self, recurlist, testing=False): """Post all subscription requests.""" for recur in recurlist: success, reason, response, subscription_id = self.process_recurring_subscription( recur, testing=testing) if success: if not testing: record_payment(self.order, self.settings, amount=recur['charged_today'], transaction_id=subscription_id) else: self.log.info( "Failed to process recurring subscription, %s: %s", reason, response) break return success, reason, response
def process(self): # process the transaction through tclink result = tclink.send(self.transactionData) status = result['status'] if status == 'approved': record_payment(self.order, self.settings, amount=self.order.balance) return (True, status, result) if status == 'decline': msg = _(u'Transaction was declined. Reason: %s' % result['declinetype']) return (False, status, msg) if status == 'baddata': msg = _(u'Improperly formatted data. Offending fields: %s' % result['offenders']) return (False, status, msg) else: msg = _(u'An error occurred: %s' % result['errortype']) return (False, status, msg)
def apply_to_order(self, order): """Apply up to the full amount of the balance of this cert to the order. Returns new balance. """ amount = min(order.balance, self.balance) log.info('applying %s from giftcert #%i [%s] to order #%i [%s]', money_format(amount), self.id, money_format(self.balance), order.id, money_format(order.balance)) config = config_get_group('PAYMENT_GIFTCERTIFICATE') orderpayment = record_payment(order, config, amount) return self.use(amount, orderpayment=orderpayment)
def success(request): """ The order has been succesfully processed. """ session = SessionStore(session_key=request.POST.get('M_session')) transaction_id = request.POST.get('cartId') amount = request.POST.get('authAmount') request.session = session if request.POST.get('transStatus', 'N') == 'Y': order = Order.objects.get(pk=transaction_id) order.add_status(status='Processing', notes=_("Paid through WorldPay.")) request.user = order.contact.user record_payment(order, payment_module, amount=amount, transaction_id=transaction_id) for cart in Cart.objects.filter(customer=order.contact): cart.empty() return generic_success(request, template='checkout/worldpay/success.html') else: context = RequestContext(request, {'message': _('Your transaction was rejected.')}) return render_to_response('shop_404.html', context)
def process(self): """ Process the transaction and return a tuple: (success/failure, reason code, response text) Example: >>> from django.conf import settings >>> from satchmo.payment.modules.dummy.processor import PaymentProcessor >>> processor = PaymentProcessor(settings) # If using a normal payment module, data should be an Order object. >>> data = {} >>> processor.prepareData(data) >>> processor.process() (True, '0', u'Success') """ orderpayment = record_payment(self.order, self.settings, amount=self.order.balance) reason_code = "0" response_text = _("Success") return (True, reason_code, response_text)
all_results = f.read() self.log_extra('Authorize response: %s', all_results) except urllib2.URLError, ue: self.log.error("error opening %s\n%s", data['connection'], ue) return (False, 'ERROR', _('Could not talk to Authorize.net gateway')) parsed_results = all_results.split(data['configuration']['x_delim_char']) response_code = parsed_results[0] reason_code = parsed_results[1] response_text = parsed_results[3] transaction_id = parsed_results[6] success = response_code == '1' if success and not testing: self.log_extra('Success, recording payment') record_payment(self.order, self.settings, amount=data['amount'], transaction_id=transaction_id) self.log_extra("Returning success=%s, reason=%s, response_text=%s", success, reason_code, response_text) return(success, reason_code, response_text) if __name__ == "__main__": """ This is for testing - enabling you to run from the command line and make sure everything is ok """ import os from satchmo.configuration import config_get_group import config # Set up some dummy classes to mimic classes being passed through Satchmo class testContact(object):
def ipn(request): """PayPal IPN (Instant Payment Notification) Cornfirms that payment has been completed and marks invoice as paid. Adapted from IPN cgi script provided at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/456361""" payment_module = config_get_group('PAYMENT_PAYPAL') if payment_module.LIVE.value: log.debug("Live IPN on %s", payment_module.KEY.value) url = payment_module.POST_URL.value account = payment_module.BUSINESS.value else: log.debug("Test IPN on %s", payment_module.KEY.value) url = payment_module.POST_TEST_URL.value account = payment_module.BUSINESS_TEST.value PP_URL = url try: data = request.POST log.debug("PayPal IPN data: " + repr(data)) if not confirm_ipn_data(data, PP_URL): return HttpResponse() if not 'payment_status' in data or not data['payment_status'] == "Completed": # We want to respond to anything that isn't a payment - but we won't insert into our database. log.info("Ignoring IPN data for non-completed payment.") return HttpResponse() try: invoice = data['invoice'] except: invoice = data['item_number'] gross = data['mc_gross'] txn_id = data['txn_id'] if not OrderPayment.objects.filter(transaction_id=txn_id).count(): # If the payment hasn't already been processed: order = Order.objects.get(pk=invoice) order.add_status(status='Pending', notes=_("Paid through PayPal.")) payment_module = config_get_group('PAYMENT_PAYPAL') record_payment(order, payment_module, amount=gross, transaction_id=txn_id) if 'memo' in data: if order.notes: notes = order.notes + "\n" else: notes = "" order.notes = notes + _('---Comment via Paypal IPN---') + u'\n' + data['memo'] order.save() log.debug("Saved order notes from Paypal") for item in order.orderitem_set.filter(product__subscriptionproduct__recurring=True, completed=False): item.completed = True item.save() for cart in Cart.objects.filter(customer=order.contact): cart.empty() except: log.exception(''.join(format_exception(*exc_info()))) return HttpResponse()
def process(request): """`Direct HTTP server-to-server request` processing. Details returned according to https://payment-services.ingenico.com/int/en/ogone/support/guides/integration%20guides/e-commerce/transaction-feedback#feedbackparameters These can be customised, but we will assume that these ones are available. ACCEPTANCE Acceptance code returned by the acquirer AMOUNT Order amount (not multiplied by 100) BRAND Card brand (our system derives this from the card number) CARDNO Masked card number CN Cardholder/customer name CURRENCY Order currency ED Expiry date NCERROR Error code orderID Your order reference PAYID Payment reference in our system PM Payment method SHASIGN SHA signature calculated by our system (if SHA-OUT configured) STATUS Transaction status (see Status overview) TRXDATE Transaction date """ logger.debug("Process: %s", request.POST) post_data = CaseInsensitiveReadOnlyDict(request.POST) if request.method == "POST": shasign = post_data.get("SHASIGN") # Verify the Shasign if verify_shasign(shasign, post_data): try: order = Order.objects.get(id=post_data.get("orderID")) except Order.DoesNotExist: raise Http404() amount = Decimal(post_data.get("AMOUNT")) transaction_id = post_data.get("ACCEPTANCE") if order.notes is None: order.notes = "" order.notes += "\n------------------ {now} ------------------\n\n".format( now=timezone.now()) if "STATUS" in post_data: status = TRANSACTION_STATUS[int(post_data["STATUS"])]["NAME"] else: status = "Unknown" notes = """ Status: {status} Transaction date: {transaction_date} Transaction ID: {acceptance} Ingenico ID: {ingenico_id} Amount: {amount} Currency: {currency} NC Error: {nc_error} Payment method: {pm} Cardholder name: {cn} Card no: {cardno} Brand: {brand} Expiry date: {ed} """.format( status=status, transaction_date=post_data.get("TRXDATE"), acceptance=transaction_id, ingenico_id=post_data.get("PAYID"), amount=post_data.get("amount"), currency=post_data.get("CURRENCY"), nc_error=post_data.get("NCERROR"), pm=post_data.get("PM"), cn=post_data.get("CN"), cardno=post_data.get("CARDNO"), brand=post_data.get("BRAND"), ed=post_data.get("ED"), ) order.notes += notes order.save() # Ensure the status is ok before recording payment # https://payment-services.ingenico.com/int/en/ogone/support/guides/user%20guides/statuses-and-errors if "STATUS" in post_data: status = int(post_data["STATUS"]) if status == 9: # Payment requested, ok to send package record_payment( order, payment_module, amount=amount, transaction_id=transaction_id, ) complete_order(order) order.add_status(status="Processing", notes=_("Payment complete.")) elif status == 1: # Cancelled by customer if order.frozen is False: order.freeze() restock_order(order) order.save() order.add_status(status="Cancelled", notes=_("")) elif status == 7 or status == 8: # Payment Deleted or Refunded if order.frozen is False: order.freeze() order.save() order.add_status(status="Refunded", notes=_("")) OrderRefund.objects.create( order=order, amount=amount, exchange_rate=order.exchange_rate, transaction_id=transaction_id, ) elif status == 2: # Authorisation refused if order.frozen is False: order.freeze() restock_order(order) order.save() order.add_status( status="Authorisation refused", notes=_("Please contact your bank or card issuer."), ) return HttpResponse() else: logger.warning("Verification failed: %s", pprint.pformat(post_data.items())) return HttpResponsePaymentRequired() else: return HttpResponseMethodNotAllowed()
def process(request): """`Direct HTTP server-to-server request` processing. Details returned according to https://payment-services.ingenico.com/int/en/ogone/support/guides/integration%20guides/e-commerce/transaction-feedback#feedbackparameters These can be customised, but we will assume that these ones are available. ACCEPTANCE Acceptance code returned by the acquirer AMOUNT Order amount (not multiplied by 100) BRAND Card brand (our system derives this from the card number) CARDNO Masked card number CN Cardholder/customer name CURRENCY Order currency ED Expiry date NCERROR Error code orderID Your order reference PAYID Payment reference in our system PM Payment method SHASIGN SHA signature calculated by our system (if SHA-OUT configured) STATUS Transaction status (see Status overview) TRXDATE Transaction date """ logger.debug("Process: %s", request.POST) post_data = CaseInsensitiveReadOnlyDict(request.POST) if request.method == "POST": shasign = post_data.get("SHASIGN") # Verify the Shasign if verify_shasign(shasign, post_data): try: order = Order.objects.get(id=post_data.get("orderID")) except Order.DoesNotExist: raise Http404() amount = Decimal(post_data.get("AMOUNT")) transaction_id = post_data.get("ACCEPTANCE") if order.notes is None: order.notes = "" order.notes += "\n------------------ {now} ------------------\n\n".format( now=timezone.now() ) if "STATUS" in post_data: status = TRANSACTION_STATUS[int(post_data["STATUS"])]["NAME"] else: status = "Unknown" notes = """ Status: {status} Transaction date: {transaction_date} Transaction ID: {acceptance} Ingenico ID: {ingenico_id} Amount: {amount} Currency: {currency} NC Error: {nc_error} Payment method: {pm} Cardholder name: {cn} Card no: {cardno} Brand: {brand} Expiry date: {ed} """.format( status=status, transaction_date=post_data.get("TRXDATE"), acceptance=transaction_id, ingenico_id=post_data.get("PAYID"), amount=post_data.get("amount"), currency=post_data.get("CURRENCY"), nc_error=post_data.get("NCERROR"), pm=post_data.get("PM"), cn=post_data.get("CN"), cardno=post_data.get("CARDNO"), brand=post_data.get("BRAND"), ed=post_data.get("ED"), ) order.notes += notes order.save() # Ensure the status is ok before recording payment # https://payment-services.ingenico.com/int/en/ogone/support/guides/user%20guides/statuses-and-errors if "STATUS" in post_data: status = int(post_data["STATUS"]) if status == 9: # Payment requested, ok to send package record_payment( order, payment_module, amount=amount, transaction_id=transaction_id, ) complete_order(order) order.add_status(status="Processing", notes=_("Payment complete.")) elif status == 1: # Cancelled by customer if order.frozen is False: order.freeze() restock_order(order) order.save() order.add_status(status="Cancelled", notes=_("")) elif status == 7 or status == 8: # Payment Deleted or Refunded if order.frozen is False: order.freeze() order.save() order.add_status(status="Refunded", notes=_("")) OrderRefund.objects.create( order=order, amount=amount, exchange_rate=order.exchange_rate, transaction_id=transaction_id, ) elif status == 2: # Authorisation refused if order.frozen is False: order.freeze() restock_order(order) order.save() order.add_status( status="Authorisation refused", notes=_("Please contact your bank or card issuer."), ) return HttpResponse() else: logger.warning("Verification failed: %s", post_data) return HttpResponsePaymentRequired() else: return HttpResponseMethodNotAllowed()
self.log.error("error opening %s\n%s", data['connection'], ue) return (False, 'ERROR', _('Could not talk to Authorize.net gateway')) parsed_results = all_results.split( data['configuration']['x_delim_char']) response_code = parsed_results[0] reason_code = parsed_results[1] response_text = parsed_results[3] transaction_id = parsed_results[6] success = response_code == '1' if success and not testing: self.log_extra('Success, recording payment') record_payment(self.order, self.settings, amount=data['amount'], transaction_id=transaction_id) self.log_extra("Returning success=%s, reason=%s, response_text=%s", success, reason_code, response_text) return (success, reason_code, response_text) if __name__ == "__main__": """ This is for testing - enabling you to run from the command line and make sure everything is ok """ import os from satchmo.configuration import config_get_group import config
class PaymentProcessor(object): """ Cybersource payment processing module You must have an account with Cybersource in order to use this module """ def __init__(self, settings): self.settings = settings self.contents = '' if settings.LIVE.value: self.testflag = 'FALSE' self.connection = settings.CONNECTION.value else: self.testflag = 'TRUE' self.connection = settings.CONNECTION_TEST.value self.configuration = { 'merchantID' : settings.MERCHANT_ID.value, 'password' : settings.TRANKEY.value, } def prepareData(self, data): self.bill_to = { 'firstName' : data.contact.first_name, 'lastName' : data.contact.last_name, 'street1': data.full_bill_street, 'city': data.bill_city, 'state' : data.bill_state, 'postalCode' : data.bill_postal_code, 'country': data.bill_country, 'email' : data.contact.email, 'phoneNumber' : data.contact.primary_phone, # Can add additional info here if you want to but it's not required } exp = data.credit_card.expirationDate.split('/') self.card = { 'accountNumber' : data.credit_card.decryptedCC, 'expirationMonth' : exp[0], 'expirationYear' : exp[1], 'cvNumber' : data.credit_card.ccv } currency = self.settings.CURRENCY_CODE.value currency = currency.replace("_", "") self.purchase_totals = { 'currency' : currency, 'grandTotalAmount' : trunc_decimal(data.balance, 2), } self.order = data def process(self): """ Creates and sends XML representation of transaction to Cybersource """ # XML format is very simple, using ElementTree for generation would be overkill t = loader.get_template('checkout/cybersource/request.xml') c = Context({ 'config' : self.configuration, 'merchantReferenceCode' : self.order.id, 'billTo' : self.bill_to, 'purchaseTotals' : self.purchase_totals, 'card' : self.card, }) request = t.render(c) conn = urllib2.Request(url=self.connection, data=request) try: f = urllib2.urlopen(conn) except urllib2.HTTPError, e: # we probably didn't authenticate properly # make sure the 'v' in your account number is lowercase return(False, '999', 'Problem parsing results') f = urllib2.urlopen(conn) all_results = f.read() tree = fromstring(all_results) parsed_results = tree.getiterator('{urn:schemas-cybersource-com:transaction-data-1.26}reasonCode') try: reason_code = parsed_results[0].text except KeyError: return(False, '999', 'Problem parsing results') # Response codes available at: # http://apps.cybersource.com/library/documentation/sbc/api_guide/SB_API.pdf response_text_dict = { '100' : 'Successful transaction.', '101' : 'The request is missing one or more required fields.', '102' : 'One or more fields in the request contains invalid data.', '104' : 'The merchantReferenceCode sent with this authorization request matches the merchantReferenceCode of another authorization request that you sent in the last 15 minutes.', '150' : 'Error: General system failure. ', '151' : 'Error: The request was received but there was a server timeout. This error does not include timeouts between the client and the server.', '152' : 'Error: The request was received, but a service did not finish running in time.', '201' : 'The issuing bank has questions about the request. You do not receive an authorization code in the reply message, but you might receive one verbally by calling the processor.', '202' : 'Expired card. You might also receive this if the expiration date you provided does not match the date the issuing bank has on file.', '203' : 'General decline of the card. No other information provided by the issuing bank.', '204' : 'Insufficient funds in the account.', '205' : 'Stolen or lost card.', '207' : 'Issuing bank unavailable.', '208' : 'Inactive card or card not authorized for card-not-present transactions.', '210' : 'The card has reached the credit limit. ', '211' : 'Invalid card verification number.', '221' : 'The customer matched an entry on the processor\'s negative file.', '231' : 'Invalid account number.', '232' : 'The card type is not accepted by the payment processor.', '233' : 'General decline by the processor.', '234' : 'There is a problem with your CyberSource merchant configuration.', '235' : 'The requested amount exceeds the originally authorized amount. Occurs, for example, if you try to capture an amount larger than the original authorization amount. This reason code only applies if you are processing a capture through the API.', '236' : 'Processor Failure', '238' : 'The authorization has already been captured. This reason code only applies if you are processing a capture through the API.', '239' : 'The requested transaction amount must match the previous transaction amount. This reason code only applies if you are processing a capture or credit through the API.', '240' : 'The card type sent is invalid or does not correlate with the credit card number.', '241' : 'The request ID is invalid. This reason code only applies when you are processing a capture or credit through the API.', '242' : 'You requested a capture through the API, but there is no corresponding, unused authorization record. Occurs if there was not a previously successful authorization request or if the previously successful authorization has already been used by another capture request. This reason code only applies when you are processing a capture through the API.', '243' : 'The transaction has already been settled or reversed.', '246' : 'The capture or credit is not voidable because the capture or credit information has already been submitted to your processor. Or, you requested a void for a type of transaction that cannot be voided. This reason code applies only if you are processing a void through the API.', '247' : 'You requested a credit for a capture that was previously voided. This reason code applies only if you are processing a void through the API.', '250' : 'Error: The request was received, but there was a timeout at the payment processor.', '520' : 'The authorization request was approved by the issuing bank but declined by CyberSource based on your Smart Authorization settings.', } if response_text_dict.has_key(reason_code): response_text = response_text_dict[reason_code] else: response_text = 'Unknown failure' if reason_code == '100': record_payment(self.order, self.settings, amount=self.order.balance) return(True, reason_code, response_text) else: return(False, reason_code, response_text)
def paypal_express_pay(request): """Process the real payment on PayPal Express Checkout with DoExpressCheckoutPayment""" try: order = Order.objects.from_request(request) except Order.DoesNotExist: url = lookup_url(payment_module, 'satchmo_checkout-step1') return HttpResponseRedirect(url) tempCart = Cart.objects.from_request(request) if tempCart.numItems == 0: template = lookup_template(payment_module, 'checkout/empty_cart.html') return render_to_response(template, RequestContext(request)) # Check if the order is still valid if not order.validate(request): context = RequestContext(request, {'message': _('Your order is no longer valid.')}) return render_to_response('shop_404.html', context) payment_module = config_get_group('PAYMENT_PAYPAL_EXPRESS') #amount = '%.2f' % (order.total) paypal = PayPal(payment_module) shipping_amount = order.shipping_cost shipping_discount = order.shipping_discount if 'paypal_express_token' in request.session: paypal_express_token = request.session['paypal_express_token'] response_getDetails = paypal.GetExpressCheckoutDetails(paypal_express_token, return_all=True) params = { 'PAYERID' : response_getDetails["PAYERID"][0], 'CURRENCYCODE' : 'USD', 'AMT' : '%.2f' % order.total, 'ITEMAMT' : '%.2f' % order.sub_total, 'SHIPPINGAMT' : '%.2f' % order.shipping_cost, 'SHIPPINGDISCOUNT' : '%.2f' % order.shipping_discount, 'TAXAMT' : '%.2f' % (order.tax - order.discount), 'TOKEN' : paypal_express_token, } # This function does the payment data = paypal.DoExpressCheckoutPayment(params) #try: log.debug("PayPal Express Checkout data: " + repr(data)) if not 'RESULT' in data or not data['RESULT'] == '0': #if not 'payment_status' in data or not data['payment_status'] == "Completed": # We want to respond to anything that isn't a payment - but we won't insert into our database. log.info("Ignoring IPN data for non-completed payment.") # I show a failed payment error template = lookup_template(payment_module, 'checkout/paypal_express/failed.html') # {'ACK': 'Failure', 'TIMESTAMP': '2009-02-28T13:48:55Z', 'L_SEVERITYCODE0': 'Error', 'L_SHORTMESSAGE0': 'Transaction cannot complete.', 'L_LONGMESSAGE0': 'The transaction cannot complete successfully. Instruct the customer to use an alternative payment method.', 'VERSION': '53.0', 'BUILD': '845846', 'L_ERRORCODE0': '10417', 'CORRELATIONID': 'be804544f01'} ctx = RequestContext(request, {'order': order, 'data': repr(data), 'ack': data["RESPMSG"], #'severity_code': data["L_SEVERITYCODE0"], #'short_message': data["L_SHORTMESSAGE0"], #'long_message': data["L_LONGMESSAGE0"], #'error_code': data["L_ERRORCODE0"], }) # Li aggiungo fuori perche se ne manca uno altrimenti restituisce un keyerror try: ctx["severity_code"]= data["RESULT"] except: pass try: ctx["short_message"]= data["RESPMSG"] except: pass try: ctx["long_message"]= data["RESPMSG"] except: pass try: ctx["error_code"]=data["RESULT"] except: pass return render_to_response(template, ctx) txn_id = data['PNREF'] if not OrderPayment.objects.filter(transaction_id=txn_id).count(): # If the payment hasn't already been processed: #order = Order.objects.get(pk=invoice) order.add_status(status='Billed', notes=_("Paid through PayPal Express Checkout.")) #orderstatus = order.status record_payment(order, payment_module, amount=order.total, transaction_id=txn_id) notes_changed = False if order.notes: notes = order.notes + "\n" else: notes = "" # Retrieving notes from paypal NOTE field if 'NOTE' in response_getDetails: notes_changed = True response_notes = response_getDetails['NOTE'][0] # If I get some problems, the error will include notes if I put it in a variable. notes = u"\n%s \n%s \n%s" % (notes, _('---Comment via Paypal EXPRESS CHECKOUT---') ,response_notes ) log.debug("Saved order notes from Paypal Express Checkout") # Retrieving notes from confirmation page if (request.method == "POST") and ('note' in request.POST) and (request.POST['note'] != ""): notes_changed = True #notes = notes + u'\n\n' + _('---Notes sent by confirm Order Form---') + u'\n' + request.POST['note'] notes = u"%s \n\n%s \n %s" % (notes, _('---Notes sent by confirm Order Form---'), request.POST['note']) log.debug("Saved order notes from Confirm Order Page") # If I must add some notes to my order if notes_changed: order.notes = notes order.save() for item in order.orderitem_set.filter(product__subscriptionproduct__recurring=True, completed=False): item.completed = True item.save() for cart in Cart.objects.filter(customer=order.contact): cart.empty() # I remove the token from the session request.session['paypal_express_token'] #order.save() #except: # log.exception(''.join(format_exception(*exc_info()))) # assert False url = urlresolvers.reverse("PAYPAL_EXPRESS_satchmo_checkout-success") return HttpResponseRedirect(url)
def ipn(request): """PayPal IPN (Instant Payment Notification) Cornfirms that payment has been completed and marks invoice as paid. Adapted from IPN cgi script provided at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/456361""" payment_module = config_get_group('PAYMENT_PAYPAL') if payment_module.LIVE.value: log.debug("PayPal IPN: Live IPN on %s", payment_module.KEY.value) url = payment_module.POST_URL.value else: log.debug("PayPal IPN: Test IPN on %s", payment_module.KEY.value) url = payment_module.POST_TEST_URL.value PP_URL = url try: data = request.POST log.debug("PayPal IPN: Data: " + repr(data)) if not confirm_ipn_data(data, PP_URL): return HttpResponse() try: invoice = data['invoice'] except KeyError: invoice = data['item_number'] if 'payment_status' not in data or not data[ 'payment_status'] == "Completed": # We want to respond to anything that isn't a payment - but we won't insert into our database. order = Order.objects.get(pk=invoice) if data['payment_status'] == "Pending": notes = "Pending Reason: %s" % (data['pending_reason']) else: notes = "" order.add_status(status=data['payment_status'], notes=(notes)) log.info( "PayPal IPN: Ignoring IPN data for non-completed payment.") return HttpResponse() gross = data['mc_gross'] txn_id = data['txn_id'] log.info("PayPal IPN: Set %s to Processing" % txn_id) if not OrderPayment.objects.filter(transaction_id=txn_id).count(): # If the payment hasn't already been processed: order = Order.objects.get(pk=invoice) order.freeze() order.add_status(status='Processing', notes=_("Paid through PayPal.")) payment_module = config_get_group('PAYMENT_PAYPAL') record_payment(order, payment_module, amount=gross, transaction_id=txn_id) # Track total sold for each product log.debug("PayPal IPN: Set quantities for %s" % txn_id) for item in order.orderitem_set.all(): if item.stock_updated is False: product = item.product product.total_sold += item.quantity product.items_in_stock -= item.quantity product.save() item.stock_updated = True item.save() log.debug("PayPal IPN: Set quantities for %s to %s" % (product, product.items_in_stock)) if 'memo' in data: if order.notes: notes = order.notes + "\n" else: notes = "" order.notes = notes + _( '---Comment via Paypal IPN---') + u'\n' + data['memo'] log.debug("PayPal IPN: Saved order notes from Paypal") for item in order.orderitem_set.filter( product__subscriptionproduct__recurring=True, completed=False): item.completed = True item.save() for cart in Cart.objects.filter(customer=order.contact): cart.empty() order.save() except: log.exception(''.join(format_exception(*exc_info()))) return HttpResponse()