def fulfill_order(request_data): """ Fulfill an order for end user purchase of a Product. Args: request_data (dict): Request data from CyberSource """ # First, save this information in a receipt receipt = Receipt.objects.create(data=request_data) # Link the order with the receipt if we can parse it reference_number = request_data["req_reference_number"] req_bill_to_email = request_data.get("req_bill_to_email") order = Order.objects.get_by_reference_number(reference_number) receipt.order = order receipt.save() new_order_status = determine_order_status_change(order, request_data["decision"]) if new_order_status is None: # This is a duplicate message, ignore since it's already handled return order.status = new_order_status order.save() sync_hubspot_deal(order) if order.status == Order.FULFILLED: complete_order(order) if settings.ENABLE_ORDER_RECEIPTS: send_ecommerce_order_receipt( order=order, cyber_source_provided_email=req_bill_to_email) # Save to log everything to an audit table including enrollments created in complete_order order.save_and_log(None)
def post( self, request, *args, **kwargs ): # pylint: disable=too-many-locals,unused-argument """ Create a new unfulfilled Order from the user's basket and return information used to submit to CyberSource. """ validated_basket = validate_basket_for_checkout(request.user) affiliate_id = get_affiliate_id_from_request(request) order = create_unfulfilled_order(validated_basket, affiliate_id=affiliate_id) base_url = request.build_absolute_uri("/") text_id = validated_basket.product_version.product.content_object.text_id receipt_url = make_receipt_url(base_url=base_url, readable_id=text_id) user_ip, _ = get_client_ip(request) if order.total_price_paid == 0: # If price is $0, don't bother going to CyberSource, just mark as fulfilled order.status = Order.FULFILLED order.save() sync_hubspot_deal(order) complete_order(order) order.save_and_log(request.user) product = validated_basket.product_version.product # $0 orders do not go to CyberSource so we need to build a payload # for GTM in order to track these purchases as well. Actual tracking # call is sent from the frontend. payload = { "transaction_id": "T-{}".format(order.id), "transaction_total": 0.00, "product_type": product.type_string, "courseware_id": text_id, "reference_number": "REF-{}".format(order.id), } # This redirects the user to our order success page url = receipt_url if settings.ENABLE_ORDER_RECEIPTS: send_ecommerce_order_receipt(order) method = "GET" else: # This generates a signed payload which is submitted as an HTML form to CyberSource cancel_url = urljoin(base_url, "checkout/") payload = generate_cybersource_sa_payload( order=order, receipt_url=receipt_url, cancel_url=cancel_url, ip_address=user_ip, ) url = settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL method = "POST" return Response({"payload": payload, "url": url, "method": method})
def create_unfulfilled_order(validated_basket, affiliate_id=None): """ Create a new Order which is not fulfilled for a purchasable Product. Note that validation should be done in the basket REST API so the validation is not done here (different from MicroMasters). Args: validated_basket (ValidatedBasket): The validated Basket and related objects affiliate_id (Optional[int]): The id of the Affiliate record to associate with this order Returns: Order: A newly created Order for the Product in the basket """ with transaction.atomic(): total_price_paid = get_product_version_price_with_discount( coupon_version=validated_basket.coupon_version, product_version=validated_basket.product_version, ) order = Order.objects.create( status=Order.CREATED, purchaser=validated_basket.basket.user, total_price_paid=total_price_paid, ) line = Line.objects.create( order=order, product_version=validated_basket.product_version, quantity=validated_basket.basket_item.quantity, ) if validated_basket.basket_item.program_run: ProgramRunLine.objects.create( line=line, program_run=validated_basket.basket_item.program_run) if validated_basket.run_selection_ids: LineRunSelection.objects.bulk_create( LineRunSelection(line=line, run_id=run_id) for run_id in validated_basket.run_selection_ids) if validated_basket.coupon_version: redeem_coupon(coupon_version=validated_basket.coupon_version, order=order) if affiliate_id is not None: AffiliateReferralAction.objects.create(affiliate_id=affiliate_id, created_order=order) sync_hubspot_deal(order) return order
def save_model(self, request, obj, form, change): """ Saves object and logs change to object """ super().save_model(request, obj, form, change) sync_hubspot_deal(obj)