Skip to content

Commit 8babe20

Browse files
authored
UR-4540 Fix - Local currency amount not applied on upgrade/multiple-membership purchase and related coupon, tax, and display gaps (#1340)
1 parent 2e65eb5 commit 8babe20

13 files changed

Lines changed: 596 additions & 48 deletions

File tree

assets/js/modules/membership/frontend/user-registration-membership-frontend.js

Lines changed: 291 additions & 7 deletions
Large diffs are not rendered by default.

includes/class-ur-smart-tags.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,14 +1064,24 @@ function ( &$value ) {
10641064
$order_detail = $orders_repository->get_order_detail( $latest_order['ID'] );
10651065
if ( ! empty( $order_detail ) && isset( $order_detail['total_amount'] ) ) {
10661066
$renewal_amount = $order_detail['total_amount'];
1067+
1068+
if ( ! empty( $order_detail['order_id'] ) ) {
1069+
$renewal_order_currency = $orders_repository->get_order_meta_by_order_id_and_meta_key( $order_detail['order_id'], 'local_currency' );
1070+
}
10671071
}
10681072
}
10691073
}
10701074
}
10711075
if ( ! empty( $renewal_amount ) ) {
1072-
// Format amount with currency if available.
1073-
$currency = get_option( 'user_registration_payment_currency', 'USD' );
1074-
if ( function_exists( 'ur_payment_integration_get_currencies' ) ) {
1076+
// Prefer the order's own currency over the store's global default.
1077+
$currency = ! empty( $renewal_order_currency['meta_value'] )
1078+
? $renewal_order_currency['meta_value']
1079+
: get_option( 'user_registration_payment_currency', 'USD' );
1080+
1081+
if ( function_exists( 'ur_get_currency_symbol' ) ) {
1082+
$symbol = ur_get_currency_symbol( $currency );
1083+
$renewal_amount = $symbol . number_format( floatval( $renewal_amount ), 2 );
1084+
} elseif ( function_exists( 'ur_payment_integration_get_currencies' ) ) {
10751085
$currencies = ur_payment_integration_get_currencies();
10761086
$symbol = isset( $currencies[ $currency ]['symbol'] ) ? $currencies[ $currency ]['symbol'] : $currency;
10771087
$renewal_amount = $symbol . number_format( floatval( $renewal_amount ), 2 );

modules/membership/includes/AJAX.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,12 @@ function ( $field ) use ( $skippable_field_types ) {
18541854
'ur_authorize_net' => $ur_authorize_data,
18551855
);
18561856

1857+
// Forward the local-currency zone the member checked out in.
1858+
if ( ! empty( $_POST['switched_currency'] ) && ! empty( $_POST['urm_zone_id'] ) ) {
1859+
$data['switched_currency'] = sanitize_text_field( $_POST['switched_currency'] );
1860+
$data['urm_zone_id'] = sanitize_text_field( $_POST['urm_zone_id'] );
1861+
}
1862+
18571863
if ( ! empty( $_POST['coupon'] ) ) {
18581864
$data['coupon'] = sanitize_text_field( $_POST['coupon'] );
18591865
}
@@ -2125,10 +2131,21 @@ function ( $user_memberships ) {
21252131
'is_purchasing_multiple' => true,
21262132
);
21272133

2134+
// Forward the local-currency zone the member checked out in.
2135+
if ( ! empty( $_POST['switched_currency'] ) && ! empty( $_POST['urm_zone_id'] ) ) {
2136+
$data['switched_currency'] = sanitize_text_field( $_POST['switched_currency'] );
2137+
$data['urm_zone_id'] = sanitize_text_field( $_POST['urm_zone_id'] );
2138+
}
2139+
21282140
if ( ! empty( $_POST['coupon'] ) ) {
21292141
$data['coupon'] = sanitize_text_field( $_POST['coupon'] );
21302142
}
21312143

2144+
if ( ! empty( $_POST['tax_rate'] ) ) {
2145+
$data['tax_rate'] = sanitize_text_field( $_POST['tax_rate'] );
2146+
$data['tax_calculation_method'] = ! empty( $_POST['tax_calculation_method'] ) ? sanitize_text_field( $_POST['tax_calculation_method'] ) : '1';
2147+
}
2148+
21322149
if ( ! empty( $user_membership_ids ) ) {
21332150
$subscription_service = new SubscriptionService();
21342151
$status = $subscription_service->can_purchase_multiple( $data );
@@ -2790,8 +2807,24 @@ public static function validate_payment_currency() {
27902807
);
27912808
}
27922809

2810+
if ( ! ur_check_module_activation( 'local-currency' ) || ! class_exists( CoreFunctions::class ) ) {
2811+
wp_send_json_success(
2812+
array(
2813+
'message' => __( 'Currency is valid.', 'user-registration' ),
2814+
)
2815+
);
2816+
}
2817+
27932818
$zone_data = CoreFunctions::ur_get_pricing_zone_by_id( $zone_id );
2794-
$currency = $zone_data['meta']['ur_local_currency'][0];
2819+
$currency = ! empty( $zone_data['meta']['ur_local_currency'][0] ) ? $zone_data['meta']['ur_local_currency'][0] : '';
2820+
2821+
if ( empty( $currency ) ) {
2822+
wp_send_json_success(
2823+
array(
2824+
'message' => __( 'Currency is invalid.', 'user-registration' ),
2825+
)
2826+
);
2827+
}
27952828

27962829
$currency_not_supported_payment_gateways = array();
27972830

modules/membership/includes/Admin/Members/Members.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use WPEverest\URMembership\Admin\Repositories\MembershipRepository;
1414
use WPEverest\URMembership\Admin\Repositories\MembersOrderRepository;
1515
use WPEverest\URMembership\Admin\Repositories\MembersSubscriptionRepository;
16+
use WPEverest\URMembership\Admin\Repositories\OrdersRepository;
1617
use WPEverest\URMembership\Admin\Services\MembershipService;
1718

1819
if ( ! defined( 'ABSPATH' ) ) {
@@ -304,16 +305,38 @@ public function render_members_edit_page( $menu_items ) {
304305
$subscription_repository = new MembersSubscriptionRepository();
305306
$membership_repository = new MembershipRepository();
306307

307-
$member_subscription = $subscription_repository->get_member_subscription( $member_id );
308+
$member_subscriptions = $subscription_repository->get_member_subscription( $member_id );
309+
$member_subscription = ! empty( $member_subscriptions[0] ) ? $member_subscriptions[0] : array();
308310

309-
$member_membership = $membership_repository->get_single_membership_by_ID( $member_subscription['item_id'] );
311+
$member_membership = $membership_repository->get_single_membership_by_ID( $member_subscription['item_id'] ?? 0 );
310312

311-
$member_membership_details['ID'] = $member_subscription['item_id'];
313+
$member_membership_details['ID'] = $member_subscription['item_id'] ?? 0;
312314
$member_membership_details['post_title'] = $member_membership['post_title'];
313315
$member_membership_details['post_content'] = json_decode( $member_membership['post_content'], true );
314316
$member_membership_details['meta_value'] = json_decode( $member_membership['meta_value'], true );
315317

316318
$membership_price_details = apply_filters( 'build_membership_list_frontend', array( (array) $member_membership_details ) )[0];
319+
320+
// Override the plan's base price with what the member actually paid (may differ under Local Currency).
321+
if ( ! empty( $member_subscription['ID'] ) && 'free' !== ( $member_membership_details['meta_value']['type'] ?? '' ) ) {
322+
$orders_repository = new OrdersRepository();
323+
$member_order = $orders_repository->get_order_by_subscription( $member_subscription['ID'] );
324+
325+
if ( ! empty( $member_order['ID'] ) ) {
326+
$local_currency_meta = $orders_repository->get_order_meta_by_order_id_and_meta_key( $member_order['ID'], 'local_currency' );
327+
$order_currency = ! empty( $local_currency_meta['meta_value'] ) ? $local_currency_meta['meta_value'] : get_option( 'user_registration_payment_currency', 'USD' );
328+
$order_symbol = ur_get_currency_symbol( $order_currency );
329+
$order_amount_display = $order_symbol . number_format( (float) $member_order['total_amount'], 2 );
330+
331+
$duration_suffix = '';
332+
$every_pos = strpos( $membership_price_details['period'], ' ' . __( 'every', 'user-registration' ) . ' ' );
333+
if ( false !== $every_pos ) {
334+
$duration_suffix = substr( $membership_price_details['period'], $every_pos );
335+
}
336+
337+
$membership_price_details['period'] = $order_amount_display . $duration_suffix;
338+
}
339+
}
317340
}
318341
include __DIR__ . '/../Views/member-create.php';
319342
}

modules/membership/includes/Admin/Services/CouponService.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,12 @@ public function validate( $data ) {
127127
$local_currency_data = ! empty( $membership_meta['local_currency'] ) ? $membership_meta['local_currency'] : array();
128128

129129
if ( ! empty( $local_currency_data ) && ur_string_to_bool( $local_currency_data['is_enable'] ) ) {
130-
$amount = CoreFunctions::ur_get_amount_after_conversion( $amount, $currency, $pricing_data, $local_currency_data, $ur_zone_id );
130+
// Pass $membership_amount as the ratio reference so a manual (flat) local price scales with the discount.
131+
$local_full_amount = CoreFunctions::ur_get_amount_after_conversion( $membership_amount, $currency, $pricing_data, $local_currency_data, $ur_zone_id );
132+
$amount = CoreFunctions::ur_get_amount_after_conversion( $amount, $currency, $pricing_data, $local_currency_data, $ur_zone_id, $membership_amount );
133+
131134
if ( $coupon_details['coupon_discount_type'] === 'fixed' ) {
132-
$coupon_details['coupon_discount'] = CoreFunctions::ur_get_amount_after_conversion( $coupon_details['coupon_discount'], $currency, $pricing_data, $local_currency_data, $ur_zone_id );
135+
$coupon_details['coupon_discount'] = max( 0, $local_full_amount - $amount );
133136
}
134137
}
135138
}

modules/membership/includes/Admin/Services/OrderService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ public function prepare_orders_data( $data, $member_id, $subscription, $upgrade_
9393
$ur_zone_id = ! empty( $data['local_currency_details']['urm_zone_id'] ) ? $data['local_currency_details']['urm_zone_id'] : '';
9494

9595
if ( ! empty( $local_currency ) && ! empty( $ur_zone_id ) && ur_check_module_activation( 'local-currency' ) && UR_PRO_ACTIVE && class_exists( CoreFunctions::class ) ) {
96-
$currency = $local_currency;
9796
$pricing_data = CoreFunctions::ur_get_pricing_zone_by_id( $ur_zone_id );
9897
$local_currency_data = ! empty( $membership_meta['local_currency'] ) ? $membership_meta['local_currency'] : array();
9998

10099
if ( ! empty( $local_currency_data ) && ur_string_to_bool( $local_currency_data['is_enable'] ) ) {
100+
$currency = $local_currency;
101101
$total = CoreFunctions::ur_get_amount_after_conversion( $total, $currency, $pricing_data, $local_currency_data, $ur_zone_id );
102102
$local_currency_converted_amount = CoreFunctions::ur_get_amount_after_conversion( $membership_meta['amount'], $currency, $pricing_data, $local_currency_data, $ur_zone_id );
103103
}

modules/membership/includes/Admin/Services/PaymentService.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use WPEverest\URMembership\Admin\Services\Stripe\StripeService;
99
use WPEverest\URM\Mollie\Services\PaymentService as MollieService;
1010
use WPEverest\URMembership\Admin\Services\Paypal\NewPaypalService;
11+
use WPEverest\URMembership\Local_Currency\Admin\CoreFunctions;
1112

1213
class PaymentService {
1314
/**
@@ -182,7 +183,27 @@ public function build_mollie_response( $data, $subscription_id, $member_id, $res
182183
$success_params = array();
183184
$data['plan_name'] = 'membership';
184185

185-
// Apply coupon discount before tax, mirroring Stripe's approach in process_stripe_payment.
186+
$base_amount = floatval( $data['amount'] );
187+
$currency = get_option( 'user_registration_payment_currency', 'USD' );
188+
$local_currency = ! empty( $response_data['switched_currency'] ) ? $response_data['switched_currency'] : '';
189+
$ur_zone_id = ! empty( $response_data['urm_zone_id'] ) ? $response_data['urm_zone_id'] : '';
190+
$pricing_data = null;
191+
$local_currency_data = array();
192+
$local_currency_applies = false;
193+
194+
if ( ! empty( $local_currency ) && ! empty( $ur_zone_id ) && UR_PRO_ACTIVE && ur_check_module_activation( 'local-currency' ) && class_exists( CoreFunctions::class ) ) {
195+
$pricing_data = CoreFunctions::ur_get_pricing_zone_by_id( $ur_zone_id );
196+
$local_currency_data = ! empty( $data['local_currency'] ) ? $data['local_currency'] : array();
197+
198+
if ( ! empty( $local_currency_data ) && ur_string_to_bool( $local_currency_data['is_enable'] ) ) {
199+
$currency = $local_currency;
200+
$local_currency_applies = true;
201+
$data['amount'] = CoreFunctions::ur_get_amount_after_conversion( $base_amount, $currency, $pricing_data, $local_currency_data, $ur_zone_id );
202+
}
203+
}
204+
205+
$data['currency'] = $currency;
206+
186207
// Skip for upgrades: SubscriptionService already bakes the coupon into chargeable_amount.
187208
$coupon_discount = 0;
188209
if ( ur_check_module_activation( 'coupon' ) && ! empty( $data['coupon'] ) && empty( $data['upgrade'] ) ) {
@@ -200,6 +221,21 @@ public function build_mollie_response( $data, $subscription_id, $member_id, $res
200221
$coupon_discount = ( 'fixed' === $coupon_details['coupon_discount_type'] )
201222
? floatval( $coupon_details['coupon_discount'] )
202223
: floatval( $data['amount'] ) * floatval( $coupon_details['coupon_discount'] ) / 100;
224+
225+
// Fixed discounts are configured in the base currency — convert before subtracting.
226+
if ( $local_currency_applies && 'fixed' === $coupon_details['coupon_discount_type'] ) {
227+
$discounted_base = max( 0, $base_amount - $coupon_discount );
228+
$discounted_local = CoreFunctions::ur_get_amount_after_conversion(
229+
$discounted_base,
230+
$currency,
231+
$pricing_data,
232+
$local_currency_data,
233+
$ur_zone_id,
234+
$base_amount
235+
);
236+
$coupon_discount = max( 0, floatval( $data['amount'] ) - $discounted_local );
237+
}
238+
203239
$data['amount'] = max( 0, floatval( $data['amount'] ) - $coupon_discount );
204240
}
205241
}

modules/membership/includes/Admin/Services/Paypal/NewPaypalService.php

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,17 +255,23 @@ private function prepare_paypal_context( $data, $membership, $member_email, $sub
255255
$membership_process = urm_get_membership_process( $member_id );
256256
$is_renewing = ! empty( $membership_process['renew'] ) && in_array( $data['current_membership_id'], $membership_process['renew'], true );
257257

258-
$currency = get_option( 'user_registration_payment_currency', 'USD' );
259-
$local_currency = isset( $response_data['switched_currency'] ) ? $response_data['switched_currency'] : '';
260-
$ur_zone_id = isset( $response_data['urm_zone_id'] ) ? $response_data['urm_zone_id'] : '';
258+
$base_membership_amount = $membership_amount;
259+
260+
$currency = get_option( 'user_registration_payment_currency', 'USD' );
261+
$local_currency = isset( $response_data['switched_currency'] ) ? $response_data['switched_currency'] : '';
262+
$ur_zone_id = isset( $response_data['urm_zone_id'] ) ? $response_data['urm_zone_id'] : '';
263+
$pricing_data = null;
264+
$local_currency_data = array();
265+
$local_currency_applies = false;
261266

262267
if ( ! empty( $local_currency ) && ! empty( $ur_zone_id ) && ur_check_module_activation( 'local-currency' ) ) {
263268
$pricing_data = CoreFunctions::ur_get_pricing_zone_by_id( $ur_zone_id );
264269
$local_currency_data = ! empty( $data['local_currency'] ) ? $data['local_currency'] : array();
265270

266271
if ( ! empty( $local_currency_data ) && ur_string_to_bool( $local_currency_data['is_enable'] ) ) {
267-
$currency = $local_currency;
268-
$membership_amount = CoreFunctions::ur_get_amount_after_conversion(
272+
$currency = $local_currency;
273+
$local_currency_applies = true;
274+
$membership_amount = CoreFunctions::ur_get_amount_after_conversion(
269275
$membership_amount,
270276
$currency,
271277
$pricing_data,
@@ -281,7 +287,11 @@ private function prepare_paypal_context( $data, $membership, $member_email, $sub
281287
$discount_value = 0.0;
282288

283289
if ( $is_upgrading ) {
284-
$final_amount = (float) ( isset( $data['amount'] ) ? $data['amount'] : $final_amount );
290+
// $data['amount'] is the prorated delta in the base currency — scale it by the plan's local/base ratio.
291+
$chargeable_amount_base = (float) ( isset( $data['amount'] ) ? $data['amount'] : $final_amount );
292+
$final_amount = ( $local_currency_applies && $base_membership_amount > 0 )
293+
? $membership_amount * ( $chargeable_amount_base / $base_membership_amount )
294+
: $chargeable_amount_base;
285295
} elseif ( ! empty( $data['coupon'] ) && ur_check_module_activation( 'coupon' ) ) {
286296
$coupon_service = new CouponService();
287297
$coupon_validation = $coupon_service->validate(
@@ -292,11 +302,26 @@ private function prepare_paypal_context( $data, $membership, $member_email, $sub
292302
);
293303

294304
if ( $coupon_validation['status'] ) {
295-
$coupon_details = ur_get_coupon_details( $data['coupon'] );
296-
$discount_value = ( 'fixed' === ( isset( $coupon_details['coupon_discount_type'] ) ? $coupon_details['coupon_discount_type'] : '' ) )
305+
$coupon_details = ur_get_coupon_details( $data['coupon'] );
306+
$coupon_discount_type = isset( $coupon_details['coupon_discount_type'] ) ? $coupon_details['coupon_discount_type'] : '';
307+
$discount_value = ( 'fixed' === $coupon_discount_type )
297308
? (float) ( isset( $coupon_details['coupon_discount'] ) ? $coupon_details['coupon_discount'] : 0 )
298309
: ( $final_amount * (float) ( isset( $coupon_details['coupon_discount'] ) ? $coupon_details['coupon_discount'] : 0 ) / 100 );
299310

311+
// Fixed discounts are configured in the base currency — convert before subtracting.
312+
if ( $local_currency_applies && 'fixed' === $coupon_discount_type ) {
313+
$discounted_base = max( 0.0, $base_membership_amount - $discount_value );
314+
$discounted_local = CoreFunctions::ur_get_amount_after_conversion(
315+
$discounted_base,
316+
$currency,
317+
$pricing_data,
318+
$local_currency_data,
319+
$ur_zone_id,
320+
$base_membership_amount
321+
);
322+
$discount_value = max( 0.0, $final_amount - $discounted_local );
323+
}
324+
300325
$final_amount = max( 0.0, (float) user_registration_sanitize_amount( $final_amount - $discount_value ) );
301326
}
302327
}

0 commit comments

Comments
 (0)