Great write-up Jeremy, thanks a lot for sharing that. The art rebellion site looks great too!
On Wed, Apr 16, 2014 at 10:27 AM, Jeremy Epstein <[email protected]>wrote: > I've published a more detailed write-up of the process I went through, > you can find it here: > > > http://greenash.net.au/thoughts/2014/03/using-paypal-wps-with-cartridge-mezzanine-django/ > > Luc, looking forward to reading more details about how you went about > it too. And I'm sure other devs would also appreciate it - then > they'll have several options to choose from, for implementing PayPal > WPS with Cartridge. > > I think that both approaches have their disadvantages. With mine, > there's code repetition - I copied the code from Cartridge's order > complete method and modified it. With yours, Luc, there's a fake > request object being used. > > Ideally, the architecture of Cartridge's order completion system will > be modified for the better, such that (a) it can be called easily from > an external code snippet like a PayPal IPN callback, and (b) it's > de-coupled from the request, and there's no need to use a fake request > object. That way, both of our hacks / solutions will become > unnecessary. > > Cheers, > Jeremy. > > On Mon, Jan 27, 2014 at 6:49 PM, Luc Milland <[email protected]> wrote: > > Hello, > > awesome, I eventually ended with something *very* similar. > > I was planning to expose my solution after the shop I am working on is > > finished (I am very late on this project). > > I made a lot of other changes and will have some suggestion to improve > > Cartridge. > > > > Regarding, the external payment stuff, we basically made the *same > > thing* : using signal in my_app/models.py, retrieve order through ipn > > stuff (I used order_uuid alone since I don't really now if it's a good > > idea to let session keys travel through requests) and close the order. > > to close the order, I did not mimic order.complete() code, but I call > > the original code through a fake request. It's something like : > > > > fake_request = HttpRequest() > > fake_request.session = session > > fake_request.cart = cart > > order.complete(fake_request) > > session.save() > > > > I tried to implement some kind of IPN order status handling to complete > > order only when status == "Completed" and not "Pending", and to trap > > error to (for now, any other IPN status than completed or pending leads > > to cancel the order). > > > > E-mails are sent on order complete or cancel. > > > > Besides, I use signals on order status transitions to have mail sent > > automatically. > > > > Finally, I wrote a little app to handle shipping services but I will > > expose this later, since it's a whole subject and would require some > > more code to be really usefull (basically, I would need another checkout > > step to handle shipping service choice). > > This is called ponyexpress and it allows to define shipping services > > through admin. Each shipping service is associated to destination zones > > (countries, for now) which are bound to price ranges depending on > > weight. > > Adding a weight field to ProductVariation then allow to calculate > > shipping cost automatically for the chosen service, depending on > > customer country. > > While no order is made, the customer country is guessed from IP so an > > estimated shipping cost can be provided in the shopping cart. > > I find this pretty cool :) > > > > I will publish this code and discuss it as soon as I have finish the > > shop I am working on. > > > > hope that will help too ! > > > > Luc > > > > Le dimanche 26 janvier 2014 à 19:04 -0800, Jeremy Epstein a écrit : > >> Hi, > >> > >> I've just finished building a site powered by Mezzanine / Cartridge, and > >> I've managed to integrate it with PayPal WPS (i.e. after completing > >> checkout, user is redirected to PayPal for payment, no SSL cert needed > on > >> the site). > >> > >> I'm going to do a more detailed write-up when I get the chance - but for > >> now, here's roughly what I did to cover the all-import "final missing > step" > >> that's being discussed in this thread: > >> > >> 1. Install cartridge-payments and django-paypal, make sure they're > enabled: > >> > >> INSTALLED_APPS = [ > >> # ... > >> "payments.multipayments", > >> "paypal.standard.ipn", > >> # ... > >> ] > >> > >> (follow other steps in the install instructions for these apps, I'm not > >> going to go through it all here). > >> > >> 2. Make sure you set PAYPAL_IPN_URL as follows: > >> > >> PAYPAL_IPN_URL = lambda cart, uuid, order_form: > >> ('paypal.standard.ipn.views.ipn', None, {}) > >> > >> 3. Place the following code somewhere in your codebase (per the > >> django-paypal docs, I placed it in the models.py file for one of my > apps): > >> > >> # ... > >> > >> from importlib import import_module > >> > >> from mezzanine.conf import settings > >> > >> from cartridge.shop.models import Cart, Order, ProductVariation, > >> DiscountCode > >> from paypal.standard.ipn.signals import payment_was_successful > >> > >> # ... > >> > >> > >> def payment_complete(sender, **kwargs): > >> """Performs the same logic as the code in > >> cartridge.shop.models.Order.complete(), but fetches the session, order, > and > >> cart objects from storage, rather than relying on the request object > being > >> passed in (which it isn't, since this is triggered on PayPal IPN > >> callback).""" > >> > >> ipn_obj = sender > >> > >> if ipn_obj.custom and ipn_obj.invoice: > >> s_key, cart_pk = ipn_obj.custom.split(',') > >> SessionStore = > import_module(settings.SESSION_ENGINE).SessionStore > >> session = SessionStore(s_key) > >> > >> try: > >> cart = Cart.objects.get(id=cart_pk) > >> try: > >> order = > Order.objects.get(transaction_id=ipn_obj.invoice) > >> for field in order.session_fields: > >> if field in session: > >> del session[field] > >> try: > >> del session["order"] > >> except KeyError: > >> pass > >> > >> # Since we're manually changing session data outside of > >> # a normal request, need to force the session object to > >> # save after modifying its data. > >> session.save() > >> > >> for item in cart: > >> try: > >> variation = > >> ProductVariation.objects.get(sku=item.sku) > >> except ProductVariation.DoesNotExist: > >> pass > >> else: > >> variation.update_stock(item.quantity * -1) > >> variation.product.actions.purchased() > >> > >> code = session.get('discount_code') > >> if code: > >> > DiscountCode.objects.active().filter(code=code).update( > >> uses_remaining=F('uses_remaining') - 1) > >> cart.delete() > >> except Order.DoesNotExist: > >> pass > >> except Cart.DoesNotExist: > >> pass > >> > >> payment_was_successful.connect(payment_complete) > >> > >> 4. Apply some hacks to cartridge-payments and django-paypal: > >> > >> a) lib/python2.7/site-packages/payments/multipayments/forms/paypal.py > >> modify per diff at: > >> > >> > https://github.com/django-mezzanine-paypal/cartridge-payments/commit/6446f09832135e10ebae5cf10c573cafd3607b9b > >> > >> b) src/django-paypal/paypal/standard/forms.py modify per: > >> > >> http://stackoverflow.com/a/16804992/2066849 > >> > >> I think that's about it - will test more thoroughly and will re-create > when > >> I do a better write-up. But basically, with this, the PayPal IPN > callback > >> does everything that cartridge.shop.models.Order.complete() would > usually > >> do, if it got called (which it doesn't if you're using PayPal WPS, and I > >> believe also PayPal EC). > >> > >> Hope that helps you guys for now. > >> > >> Jeremy. > >> > >> On Thursday, November 14, 2013 2:08:30 AM UTC+11, Luc Milland wrote: > >> > > >> > Hello, > >> > I am trying to make a shop with cartridge but I don't want to deal > with > >> > credit cards. > >> > That's why I want to use cartridge-payment with Paypal express > checkout > >> > (payment part only, if I understand well). > >> > Using sandbox paypals accounts and cartridge-payment readme, I can > make > >> > an order, pay it and be sent back to the shop at > >> > shop/checkout/complete . > >> > But there is my problem : the order is registred, but not completed. I > >> > mean that it looks as the final checkout step did not apply (I guess > it > >> > is because the checkout form is sent to paypal and to our good old > >> > checkout_step view, right ?) and as order.complete(request) was never > >> > called. > >> > As a result, cart is not empty, etc... > >> > Does anybody had already been there ? Did I miss something ? Is there > a > >> > way to jump back in the checkout workflow, maybe with Paypal IPN ? > >> > > >> > thanks for your ideas and many thanks to all who build this awesome > >> > piece of code that mezzanine/cartridge is. > >> > > >> > Luc > >> > > >> > > >> On Thursday, November 14, 2013 2:08:30 AM UTC+11, Luc Milland wrote: > >> > > >> > Hello, > >> > I am trying to make a shop with cartridge but I don't want to deal > with > >> > credit cards. > >> > That's why I want to use cartridge-payment with Paypal express > checkout > >> > (payment part only, if I understand well). > >> > Using sandbox paypals accounts and cartridge-payment readme, I can > make > >> > an order, pay it and be sent back to the shop at > >> > shop/checkout/complete . > >> > But there is my problem : the order is registred, but not completed. I > >> > mean that it looks as the final checkout step did not apply (I guess > it > >> > is because the checkout form is sent to paypal and to our good old > >> > checkout_step view, right ?) and as order.complete(request) was never > >> > called. > >> > As a result, cart is not empty, etc... > >> > Does anybody had already been there ? Did I miss something ? Is there > a > >> > way to jump back in the checkout workflow, maybe with Paypal IPN ? > >> > > >> > thanks for your ideas and many thanks to all who build this awesome > >> > piece of code that mezzanine/cartridge is. > >> > > >> > Luc > >> > > >> > > >> > > > > > > -- > > You received this message because you are subscribed to a topic in the > Google Groups "Mezzanine Users" group. > > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/mezzanine-users/n2d6SODIF1o/unsubscribe. > > To unsubscribe from this group and all its topics, send an email to > [email protected]. > > For more options, visit https://groups.google.com/groups/opt_out. > > -- > You received this message because you are subscribed to the Google Groups > "Mezzanine Users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout. > -- Stephen McDonald http://jupo.org -- You received this message because you are subscribed to the Google Groups "Mezzanine Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
