Passed
Push — master ( bcec86...578d12 )
by Brian
10:46
created

process_discount()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 23
rs 10
cc 3
nc 3
nop 3
1
<?php
2
/**
3
 * Processes discounts for a payment form submission.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * Payment form submission discount class
11
 *
12
 */
13
class GetPaid_Payment_Form_Submission_Discount {
14
15
	/**
16
	 * Submission discounts.
17
	 * @var array
18
	 */
19
	public $discounts = array();
20
21
    /**
22
	 * Class constructor
23
	 *
24
	 * @param GetPaid_Payment_Form_Submission $submission
25
	 * @param float                           $initial_total
26
	 * @param float                           $recurring_total
27
	 */
28
	public function __construct( $submission, $initial_total, $recurring_total ) {
29
30
		// Process any existing invoice discounts.
31
		if ( $submission->has_invoice() ) {
32
			$this->discounts = $submission->get_invoice()->get_discounts();
33
		}
34
35
		// Do we have a discount?
36
		$discount = $submission->get_field( 'discount' );
37
38
		if ( empty( $discount ) ) {
39
40
			if ( isset( $this->discounts['discount_code'] ) ) {
41
				unset( $this->discounts['discount_code'] );
42
			}
43
44
			return;
45
		}
46
47
		// Processes the discount code.
48
		$amount = max( $initial_total, $recurring_total );
49
		$this->process_discount( $submission, $discount, $amount );
50
51
	}
52
53
	/**
54
	 * Processes a submission discount.
55
	 *
56
	 * @param GetPaid_Payment_Form_Submission $submission
57
	 * @param string                          $discount
58
	 * @param float                           $amount
59
	 */
60
	public function process_discount( $submission, $discount, $amount ) {
61
62
		// Fetch the discount.
63
		$discount = new WPInv_Discount( $discount );
64
65
		// Ensure it is active.
66
        if ( ! $this->is_discount_active( $discount ) ) {
67
			throw new Exception( __( 'Invalid or expired discount code', 'invoicing' ) );
68
		}
69
70
		// Exceeded limit.
71
		if ( $discount->has_exceeded_limit() ) {
72
			throw new Exception( __( 'This discount code has been used up', 'invoicing' ) );
73
		}
74
75
		// Validate usages.
76
		$this->validate_single_use_discount( $submission, $discount );
77
78
		// Validate amount.
79
		$this->validate_discount_amount( $submission, $discount, $amount );
80
81
		// Save the discount.
82
		$this->discounts['discount_code'] = $this->calculate_discount( $submission, $discount );
83
	}
84
85
	/**
86
	 * Validates a single use discount.
87
	 *
88
	 * @param WPInv_Discount                  $discount
89
	 * @return bool
90
	 */
91
	public function is_discount_active(  $discount ) {
92
		return $discount->exists() && $discount->is_active() && $discount->has_started() && ! $discount->is_expired();
93
	}
94
95
	/**
96
	 * Returns a user's id or email.
97
	 *
98
	 * @param string $email
99
	 * @return int|string|false
100
	 */
101
	public function get_user_id_or_email( $email ) {
102
103
		if ( is_user_logged_in() ) {
104
			return get_current_user_id();
105
		}
106
107
		return empty( $email ) ? false : sanitize_email( $email );
108
	}
109
110
	/**
111
	 * Validates a single use discount.
112
	 *
113
	 * @param GetPaid_Payment_Form_Submission $submission
114
	 * @param WPInv_Discount                  $discount
115
	 */
116
	public function validate_single_use_discount( $submission, $discount ) {
117
118
		// Abort if it is not a single use discount.
119
		if ( ! $discount->is_single_use() ) {
120
			return;
121
		}
122
123
		// Ensure there is a valid billing email.
124
		$user = $this->get_user_id_or_email( $submission->get_billing_email() );
125
126
		if ( empty( $user ) ) {
127
			throw new Exception( __( 'You need to either log in or enter your billing email before applying this discount', 'invoicing' ) );
128
		}
129
130
		// Has the user used this discount code before?
131
		if ( ! $discount->is_valid_for_user( $user ) ) {
132
			throw new Exception( __( 'You have already used this discount', 'invoicing' ) );
133
		}
134
135
	}
136
137
	/**
138
	 * Validates the discount's amount.
139
	 *
140
	 * @param GetPaid_Payment_Form_Submission $submission
141
	 * @param WPInv_Discount         $discount
142
	 * @param float                  $amount
143
	 */
144
	public function validate_discount_amount( $submission, $discount, $amount ) {
145
146
		// Validate minimum amount.
147
		if ( ! $discount->is_minimum_amount_met( $amount ) ) {
148
			$min = wpinv_price( $discount->get_minimum_total(), $submission->get_currency() );
149
			throw new Exception( sprintf( __( 'The minimum total for using this discount is %s', 'invoicing' ), $min ) );
150
		}
151
152
		// Validate the maximum amount.
153
		if ( ! $discount->is_maximum_amount_met( $amount ) ) {
154
			$max = wpinv_price( $discount->get_maximum_total(), $submission->get_currency() );
155
			throw new Exception( sprintf( __( 'The maximum total for using this discount is %s', 'invoicing' ), $max ) );
156
		}
157
158
	}
159
160
	/**
161
	 * Calculates the discount code's amount.
162
	 *
163
	 * Ensure that the discount exists and has been validated before calling this method.
164
	 *
165
	 * @param GetPaid_Payment_Form_Submission $submission
166
	 * @param WPInv_Discount                  $discount
167
	 * @return array
168
	 */
169
	public function calculate_discount( $submission, $discount ) {
170
171
		$initial_discount   = 0;
172
		$recurring_discount = 0;
173
174
		foreach ( $submission->get_items() as $item ) {
175
176
			// Abort if it is not valid for this item.
177
			if ( ! $discount->is_valid_for_items( array( $item->get_id() ) ) ) {
178
				continue;
179
			}
180
181
			// Calculate the initial amount...
182
			$initial_discount += $discount->get_discounted_amount( $item->get_sub_total() );
183
184
			// ... and maybe the recurring amount.
185
			if ( $item->is_recurring() && $discount->is_recurring() ) {
186
				$recurring_discount += $discount->get_discounted_amount( $item->get_recurring_sub_total() );
187
			}
188
189
		}
190
191
		return array(
192
			'name'               => 'discount_code',
193
			'discount_code'      => $discount->get_code(),
194
			'initial_discount'   => $initial_discount,
195
			'recurring_discount' => $recurring_discount,
196
		);
197
198
	}
199
200
}
201