WooCommerce Square - Version 2.2.0

Version Description

  • 2020.08.31 =
  • Feature - Import new product variations from square to existing products in WooCommerce. PR#475
  • Feature - Variations that are removed from Square will now be removed from products in WooCommerce during import. PR#475
  • Feature - Upgrade to the Square Payments and Refunds API. PR#408
  • Feature - New orders can be refunded up to one year after payment (up from 120 days). PR#408
  • Fix - Only import products from Square that have non-empty SKUs. PR#475
  • Fix - Empty product categories imported from Square into WooCommerce. PR#475
  • Fix - Assign existing products to new categories imported from Square. PR#475
  • Fix - Prevents loading of Square Assets on all pages except My Account -> Payment Methods & Checkout. PR#469
  • Fix - Square Product Import & Product Manual Sync not triggering on mobile browsers. PR#472
  • Fix - 3D Secure Verification Token is missing, Intent mismatch and other checkout errors related to SCA for merchants outside of the EU. PR#471
  • Fix - Updated some of our documentation and support links in admin notices so they no longer redirect to an old URL or a 404 page. PR#474
  • Fix - Use pagination to fetch inventory counts from Square. PR#478
  • Fix - Display WooCommerce checkout validation errors along with Square payment form errors. PR#476
  • Fix - Switching between sandbox and production environments will now show correct business locations. PR#462
  • Fix - Don't wipe a customer's saved cards on when we receive an API error. PR#460
  • Fix - Exclude draft and pending products from syncing from WooCommerce to Square. PR#484
  • Fix - DevTools errors caused by missing minified JS files.
  • Fix - PHP errors when syncing large amounts of products (get_data() on null and getCursor() on a string). PR#497
Download this release

Release Info

Developer automattic
Plugin Icon 128x128 WooCommerce Square
Version 2.2.0
Comparing to
See all releases

Code changes from version 2.1.6 to 2.2.0

Files changed (74) hide show
  1. assets/js/admin/wc-square-admin-settings.coffee +75 -48
  2. assets/js/admin/wc-square-admin-settings.min.js +1 -1
  3. assets/js/admin/wc-square-admin-settings.min.js.map +1 -1
  4. assets/js/frontend/wc-square.coffee +56 -7
  5. assets/js/frontend/wc-square.min.js +1 -1
  6. assets/js/frontend/wc-square.min.js.map +1 -1
  7. i18n/languages/woocommerce-square.pot +225 -212
  8. includes/AJAX.php +37 -38
  9. includes/API.php +51 -42
  10. includes/API/Request.php +5 -5
  11. includes/API/Requests/Catalog.php +58 -38
  12. includes/API/Requests/Customers.php +3 -3
  13. includes/API/Requests/Inventory.php +23 -19
  14. includes/API/Requests/Locations.php +1 -1
  15. includes/API/Response.php +4 -4
  16. includes/API/Responses/Catalog.php +1 -1
  17. includes/API/Responses/Connection_Refresh_Response.php +1 -1
  18. includes/API/Responses/Inventory.php +2 -2
  19. includes/API/Responses/Locations.php +2 -2
  20. includes/Admin.php +115 -70
  21. includes/Admin/Privacy.php +7 -7
  22. includes/Admin/Settings_Page.php +6 -6
  23. includes/Admin/Sync_Page.php +47 -22
  24. includes/Emails/Access_Token_Email.php +16 -11
  25. includes/Emails/Base_Email.php +27 -20
  26. includes/Emails/Sync_Completed.php +26 -21
  27. includes/Functions.php +1 -1
  28. includes/Gateway.php +130 -41
  29. includes/Gateway/API.php +44 -8
  30. includes/Gateway/API/Requests/Customers.php +8 -9
  31. includes/Gateway/API/Requests/Orders.php +70 -44
  32. includes/Gateway/API/Requests/Payments.php +205 -0
  33. includes/Gateway/API/Requests/Refunds.php +76 -0
  34. includes/Gateway/API/Requests/Transactions.php +12 -12
  35. includes/Gateway/API/Response.php +1 -1
  36. includes/Gateway/API/Responses/Charge.php +5 -5
  37. includes/Gateway/API/Responses/Create_Customer.php +1 -1
  38. includes/Gateway/API/Responses/Create_Customer_Card.php +4 -4
  39. includes/Gateway/API/Responses/Create_Payment.php +174 -0
  40. includes/Gateway/API/Responses/Get_Customer.php +5 -5
  41. includes/Gateway/API/Responses/Refund.php +3 -4
  42. includes/Gateway/Card_Handler.php +110 -1
  43. includes/Gateway/Customer_Helper.php +24 -18
  44. includes/Gateway/Payment_Form.php +45 -36
  45. includes/Handlers/Background_Job.php +35 -24
  46. includes/Handlers/Category.php +12 -9
  47. includes/Handlers/Connection.php +28 -27
  48. includes/Handlers/Email.php +4 -4
  49. includes/Handlers/Order.php +18 -18
  50. includes/Handlers/Product.php +122 -102
  51. includes/Handlers/Product/Woo_SOR.php +26 -23
  52. includes/Handlers/Products.php +67 -56
  53. includes/Handlers/Sync.php +46 -36
  54. includes/Lifecycle.php +163 -86
  55. includes/Plugin.php +181 -105
  56. includes/Settings.php +195 -125
  57. includes/Sync/Catalog_Item.php +8 -6
  58. includes/Sync/Interval_Polling.php +44 -34
  59. includes/Sync/Job.php +1 -1
  60. includes/Sync/Manual_Synchronization.php +241 -184
  61. includes/Sync/Product_Import.php +395 -234
  62. includes/Sync/Records.php +33 -30
  63. includes/Sync/Records/Record.php +25 -22
  64. includes/Sync/Stepped_Job.php +9 -9
  65. includes/Utilities/Encryption_Utility.php +2 -3
  66. includes/Utilities/Money_Utility.php +10 -8
  67. includes/Utilities/String_Utility.php +82 -0
  68. readme.txt +23 -3
  69. templates/emails/plain/square-email.php +1 -1
  70. templates/emails/square-email.php +1 -1
  71. vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/assets/js/frontend/sv-wc-payment-gateway-apple-pay.min.js.map +10 -0
  72. vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/assets/js/frontend/sv-wc-payment-gateway-my-payment-methods.min.js.map +10 -0
  73. vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/assets/js/frontend/sv-wc-payment-gateway-payment-form.min.js.map +10 -0
  74. woocommerce-square.php +29 -17
assets/js/admin/wc-square-admin-settings.coffee CHANGED
@@ -15,6 +15,13 @@ jQuery( document ).ready ( $ ) ->
15
  if ( 'woocommerce_page_wc-settings' isnt pagenow )
16
  return
17
 
 
 
 
 
 
 
 
18
 
19
  # toggle the "hide products" setting depending on the SOR
20
  $( '#wc_square_system_of_record' ).change( ->
@@ -56,26 +63,6 @@ jQuery( document ).ready ( $ ) ->
56
  e.preventDefault()
57
  $( 'button.modal-close' ).trigger( 'click' )
58
 
59
- $( '#btn-ok' ).on 'click', ( e ) ->
60
- e.preventDefault()
61
- $( this ).unbind()
62
-
63
- data =
64
- action : 'wc_square_import_products_from_square'
65
- dispatch : wc_square_admin_settings.sync_in_background
66
- security : wc_square_admin_settings.import_products_from_square
67
-
68
- $.post wc_square_admin_settings.ajax_url, data, ( response ) ->
69
-
70
- message = if response.data then response.data else null
71
-
72
- if response.success and message
73
- alert( message )
74
- else if not response.success and message
75
- alert( message )
76
-
77
- location.reload()
78
-
79
  # initiate a manual sync
80
  $( '#wc-square-sync' ).on 'click', ( e ) ->
81
  e.preventDefault()
@@ -89,34 +76,62 @@ jQuery( document ).ready ( $ ) ->
89
  e.preventDefault()
90
  $( 'button.modal-close' ).trigger( 'click' )
91
 
92
- # upon confirming, start a background process to sync products with Square
93
- $( '#btn-ok' ).on 'click', ( e ) ->
94
- e.preventDefault()
95
- $( this ).unbind()
96
-
97
- $( 'table.sync' ).block
98
- message : null
99
- overlayCSS :
100
- 'opacity' : '0.2'
101
- $( 'table.records' ).block
102
- message : null
103
- overlayCSS :
104
- 'opacity' : '0.2'
105
- $( '#wc-square_clear-sync-records' ).prop( 'disabled', true )
106
-
107
- data =
108
- action : 'wc_square_sync_products_with_square'
109
- dispatch : wc_square_admin_settings.sync_in_background
110
- security : wc_square_admin_settings.sync_products_with_square
111
-
112
- $.post wc_square_admin_settings.ajax_url, data, ( response ) ->
113
- if response and response.success
 
 
 
 
 
114
  location.reload()
115
- else
116
- $( '#wc-square_clear-sync-records' ).prop( 'disabled', false )
117
- $( 'table.sync' ).unblock()
118
- $( 'table.records' ).unblock()
119
- console.log( response )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
 
122
  # sync record handling
@@ -194,6 +209,17 @@ jQuery( document ).ready ( $ ) ->
194
 
195
  $( 'table.records' ).unblock()
196
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
  ###*
199
  # Returns a job sync status.
@@ -234,7 +260,8 @@ jQuery( document ).ready ( $ ) ->
234
  # update progress info in table cell
235
  if 'product_import' is response.data.action
236
  progress += wc_square_admin_settings.i18n.skipped + ': ' + parseInt( response.data.skipped_products_count, 10 ) + '<br/>'
237
- progress += wc_square_admin_settings.i18n.imported + ': ' + parseInt( response.data.processed_products_count, 10 )
 
238
  else if response.data.percentage
239
  progress += parseInt( response.data.percentage, 10 ) + '%'
240
 
15
  if ( 'woocommerce_page_wc-settings' isnt pagenow )
16
  return
17
 
18
+ if not wc_square_admin_settings.is_sandbox
19
+ # Hide sandbox settings if is_sandbox is set
20
+ $( '#wc_square_sandbox_settings' ).hide()
21
+
22
+ $( '#wc_square_sandbox_settings' ).next().hide()
23
+
24
+ $( '.wc_square_sandbox_settings' ).closest( 'tr' ).hide()
25
 
26
  # toggle the "hide products" setting depending on the SOR
27
  $( '#wc_square_system_of_record' ).change( ->
63
  e.preventDefault()
64
  $( 'button.modal-close' ).trigger( 'click' )
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  # initiate a manual sync
67
  $( '#wc-square-sync' ).on 'click', ( e ) ->
68
  e.preventDefault()
76
  e.preventDefault()
77
  $( 'button.modal-close' ).trigger( 'click' )
78
 
79
+ # Listen for wc_backbone_modal_response event handler.
80
+ $( document.body ).on 'wc_backbone_modal_response', ( e, target ) ->
81
+ switch target
82
+
83
+ when 'wc-square-import-products'
84
+ # Add Block overlay since the modal exits immediately
85
+ # after wc_backbone_modal_response is triggered.
86
+ $( '#wpbody' ).block
87
+ message : null
88
+ overlayCSS :
89
+ 'opacity' : '0.2'
90
+ onBlock: ->
91
+ $( '.blockUI.blockOverlay' ).css
92
+ 'position': 'fixed'
93
+ return
94
+
95
+ data =
96
+ action : 'wc_square_import_products_from_square'
97
+ dispatch : wc_square_admin_settings.sync_in_background
98
+ security : wc_square_admin_settings.import_products_from_square
99
+
100
+ $.post wc_square_admin_settings.ajax_url, data, ( response ) ->
101
+ message = if response.data then response.data else null
102
+
103
+ if message
104
+ alert( message )
105
+
106
  location.reload()
107
+
108
+ when 'wc-square-sync'
109
+ $( 'table.sync' ).block
110
+ message : null
111
+ overlayCSS :
112
+ 'opacity' : '0.2'
113
+ $( 'table.records' ).block
114
+ message : null
115
+ overlayCSS :
116
+ 'opacity' : '0.2'
117
+ $( '#wc-square_clear-sync-records' ).prop( 'disabled', true )
118
+
119
+ data =
120
+ action : 'wc_square_sync_products_with_square'
121
+ dispatch : wc_square_admin_settings.sync_in_background
122
+ security : wc_square_admin_settings.sync_products_with_square
123
+
124
+ $.post wc_square_admin_settings.ajax_url, data, ( response ) ->
125
+ if response and response.success
126
+ location.reload()
127
+ else
128
+ $( '#wc-square_clear-sync-records' ).prop( 'disabled', false )
129
+ $( 'table.sync' ).unblock()
130
+ $( 'table.records' ).unblock()
131
+
132
+ else
133
+ return
134
+ return
135
 
136
 
137
  # sync record handling
209
 
210
  $( 'table.records' ).unblock()
211
 
212
+ # Add explicit square environment to post data to deal with swapping between production and sandbox in the back end
213
+ $( 'form' ).submit ( e ) ->
214
+
215
+ environment = if $( '#wc_square_enable_sandbox' ).is( ':checked' ) then 'sandbox' else 'production'
216
+
217
+ $( this ).append( $( '<input>', {
218
+ type: 'hidden',
219
+ name: 'wc_square_environment',
220
+ value: environment,
221
+ } ) )
222
+
223
 
224
  ###*
225
  # Returns a job sync status.
260
  # update progress info in table cell
261
  if 'product_import' is response.data.action
262
  progress += wc_square_admin_settings.i18n.skipped + ': ' + parseInt( response.data.skipped_products_count, 10 ) + '<br/>'
263
+ progress += wc_square_admin_settings.i18n.updated + ': ' + parseInt( response.data.updated_products_count, 10 ) + '<br/>'
264
+ progress += wc_square_admin_settings.i18n.imported + ': ' + parseInt( response.data.imported_products_count, 10 )
265
  else if response.data.percentage
266
  progress += parseInt( response.data.percentage, 10 ) + '%'
267
 
assets/js/admin/wc-square-admin-settings.min.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";(function(){jQuery(document).ready(function(e){var s,t,r,n;if(null!=(r=window.typenow)?r:"","woocommerce_page_wc-settings"===(null!=(n=window.pagenow)?n:""))return e("#wc_square_system_of_record").change(function(){var s,t,r;return r=e(this).val(),s=e("#wc_square_enable_inventory_sync"),t=s.closest("tr"),"square"===r||"woocommerce"===r?(s.next("span").html(wc_square_admin_settings.i18n.sync_inventory_label[r]),t.find(".description").html(wc_square_admin_settings.i18n.sync_inventory_description[r]),t.show()):(s.prop("checked",!1),t.hide()),"square"===r?e("#wc_square_hide_missing_products").closest("tr").show():e("#wc_square_hide_missing_products").closest("tr").hide()}).change(),e(".js-import-square-products").on("click",function(s){return s.preventDefault(),new e.WCBackboneModal.View({target:"wc-square-import-products"}),e("#btn-close").on("click",function(s){return s.preventDefault(),e("button.modal-close").trigger("click")}),e("#btn-ok").on("click",function(s){var t;return s.preventDefault(),e(this).unbind(),t={action:"wc_square_import_products_from_square",dispatch:wc_square_admin_settings.sync_in_background,security:wc_square_admin_settings.import_products_from_square},e.post(wc_square_admin_settings.ajax_url,t,function(e){var s;return s=e.data?e.data:null,e.success&&s?alert(s):!e.success&&s&&alert(s),location.reload()})})}),e("#wc-square-sync").on("click",function(s){return s.preventDefault(),new e.WCBackboneModal.View({target:"wc-square-sync"}),e("#btn-close").on("click",function(s){return s.preventDefault(),e("button.modal-close").trigger("click")}),e("#btn-ok").on("click",function(s){var t;return s.preventDefault(),e(this).unbind(),e("table.sync").block({message:null,overlayCSS:{opacity:"0.2"}}),e("table.records").block({message:null,overlayCSS:{opacity:"0.2"}}),e("#wc-square_clear-sync-records").prop("disabled",!0),t={action:"wc_square_sync_products_with_square",dispatch:wc_square_admin_settings.sync_in_background,security:wc_square_admin_settings.sync_products_with_square},e.post(wc_square_admin_settings.ajax_url,t,function(s){return s&&s.success?location.reload():(e("#wc-square_clear-sync-records").prop("disabled",!1),e("table.sync").unblock(),e("table.records").unblock(),console.log(s))})})}),t='<tr><td colspan="4"><em>'+wc_square_admin_settings.i18n.no_records_found+"</em></td></tr>",e("#wc-square_clear-sync-records").on("click",function(s){var r;return s.preventDefault(),e("table.records").block({message:null,overlayCSS:{opacity:"0.2"}}),r={action:"wc_square_handle_sync_records",id:"all",handle:"delete",security:wc_square_admin_settings.handle_sync_with_square_records},e.post(wc_square_admin_settings.ajax_url,r,function(s){return s&&s.success?(e("table.records tbody").html(t),e("#wc-square_clear-sync-records").prop("disabled",!0)):(s.data&&alert(s.data),console.log(s)),e("table.records").unblock()})}),e(".records .actions button.action").on("click",function(s){var r,n,a;return s.preventDefault(),e("table.records").block({message:null,overlayCSS:{opacity:"0.2"}}),a=e(this).data("id"),r=e(this).data("action"),n={action:"wc_square_handle_sync_records",id:a,handle:r,security:wc_square_admin_settings.handle_sync_with_square_records},e.post(wc_square_admin_settings.ajax_url,n,function(s){var n;return s&&s.success?(n="#record-"+a,"delete"===r?(e(n).remove(),e("table.records tbody tr").length||(e("table.records tbody").html(t),e("#wc-square_clear-sync-records").prop("disabled",!0))):"resolve"!==r&&"unsync"!==r||(e(n+" .type").html('<mark class="resolved"><span>'+wc_square_admin_settings.i18n.resolved+"</span></mark>"),e(n+" .actions").html("&mdash;"))):(s&&s.data&&alert(s.data),console.log({record:a,action:r,response:s})),e("table.records").unblock()})}),s=function(t){var r,n;return(r=e("span.progress"))&&0!==r.length||(e("p.sync-result").append(' <span class="progress" style="display:block"></span>'),r=e("span.progress")),n={action:"wc_square_get_sync_with_square_status",security:wc_square_admin_settings.get_sync_with_square_status_nonce,job_id:t},e.post(wc_square_admin_settings.ajax_url,n,function(t){var n,a;if(t&&t.data)return t.success&&t.data.id?(e("table.sync .spinner").css("visibility","visible"),e("#wc-square_clear-sync-records").prop("disabled",!0),e("table.records .actions button").prop("disabled",!0),"completed"!==(a=t.data.status)&&"failed"!==a?(n=" ","product_import"===t.data.action?(n+=wc_square_admin_settings.i18n.skipped+": "+parseInt(t.data.skipped_products_count,10)+"<br/>",n+=wc_square_admin_settings.i18n.imported+": "+parseInt(t.data.processed_products_count,10)):t.data.percentage&&(n+=parseInt(t.data.percentage,10)+"%"),r.html(n),setTimeout(function(){return s(t.data.id)},3e4)):location.reload()):(e("#wc-square_clear-sync-records").prop("disabled",!1),e("table.records .actions button").prop("disabled",!1),e("table.sync .spinner").css("visibility","hidden"),console.log(t))})},wc_square_admin_settings.existing_sync_job_id?s(wc_square_admin_settings.existing_sync_job_id):void 0})}).call(void 0);
2
  //# sourceMappingURL=wc-square-admin-settings.min.js.map
1
+ "use strict";(function(){jQuery(document).ready(function(e){var s,t,a,n;if(null!=(a=window.typenow)?a:"","woocommerce_page_wc-settings"===(null!=(n=window.pagenow)?n:""))return wc_square_admin_settings.is_sandbox||(e("#wc_square_sandbox_settings").hide(),e("#wc_square_sandbox_settings").next().hide(),e(".wc_square_sandbox_settings").closest("tr").hide()),e("#wc_square_system_of_record").change(function(){var s,t,a;return a=e(this).val(),s=e("#wc_square_enable_inventory_sync"),t=s.closest("tr"),"square"===a||"woocommerce"===a?(s.next("span").html(wc_square_admin_settings.i18n.sync_inventory_label[a]),t.find(".description").html(wc_square_admin_settings.i18n.sync_inventory_description[a]),t.show()):(s.prop("checked",!1),t.hide()),"square"===a?e("#wc_square_hide_missing_products").closest("tr").show():e("#wc_square_hide_missing_products").closest("tr").hide()}).change(),e(".js-import-square-products").on("click",function(s){return s.preventDefault(),new e.WCBackboneModal.View({target:"wc-square-import-products"}),e("#btn-close").on("click",function(s){return s.preventDefault(),e("button.modal-close").trigger("click")})}),e("#wc-square-sync").on("click",function(s){return s.preventDefault(),new e.WCBackboneModal.View({target:"wc-square-sync"}),e("#btn-close").on("click",function(s){return s.preventDefault(),e("button.modal-close").trigger("click")})}),e(document.body).on("wc_backbone_modal_response",function(s,t){var a;switch(t){case"wc-square-import-products":e("#wpbody").block({message:null,overlayCSS:{opacity:"0.2"},onBlock:function(){e(".blockUI.blockOverlay").css({position:"fixed"})}}),a={action:"wc_square_import_products_from_square",dispatch:wc_square_admin_settings.sync_in_background,security:wc_square_admin_settings.import_products_from_square},e.post(wc_square_admin_settings.ajax_url,a,function(e){var s;return(s=e.data?e.data:null)&&alert(s),location.reload()});break;case"wc-square-sync":e("table.sync").block({message:null,overlayCSS:{opacity:"0.2"}}),e("table.records").block({message:null,overlayCSS:{opacity:"0.2"}}),e("#wc-square_clear-sync-records").prop("disabled",!0),a={action:"wc_square_sync_products_with_square",dispatch:wc_square_admin_settings.sync_in_background,security:wc_square_admin_settings.sync_products_with_square},e.post(wc_square_admin_settings.ajax_url,a,function(s){return s&&s.success?location.reload():(e("#wc-square_clear-sync-records").prop("disabled",!1),e("table.sync").unblock(),e("table.records").unblock())});break;default:return}}),t='<tr><td colspan="4"><em>'+wc_square_admin_settings.i18n.no_records_found+"</em></td></tr>",e("#wc-square_clear-sync-records").on("click",function(s){var a;return s.preventDefault(),e("table.records").block({message:null,overlayCSS:{opacity:"0.2"}}),a={action:"wc_square_handle_sync_records",id:"all",handle:"delete",security:wc_square_admin_settings.handle_sync_with_square_records},e.post(wc_square_admin_settings.ajax_url,a,function(s){return s&&s.success?(e("table.records tbody").html(t),e("#wc-square_clear-sync-records").prop("disabled",!0)):(s.data&&alert(s.data),console.log(s)),e("table.records").unblock()})}),e(".records .actions button.action").on("click",function(s){var a,n,r;return s.preventDefault(),e("table.records").block({message:null,overlayCSS:{opacity:"0.2"}}),r=e(this).data("id"),a=e(this).data("action"),n={action:"wc_square_handle_sync_records",id:r,handle:a,security:wc_square_admin_settings.handle_sync_with_square_records},e.post(wc_square_admin_settings.ajax_url,n,function(s){var n;return s&&s.success?(n="#record-"+r,"delete"===a?(e(n).remove(),e("table.records tbody tr").length||(e("table.records tbody").html(t),e("#wc-square_clear-sync-records").prop("disabled",!0))):"resolve"!==a&&"unsync"!==a||(e(n+" .type").html('<mark class="resolved"><span>'+wc_square_admin_settings.i18n.resolved+"</span></mark>"),e(n+" .actions").html("&mdash;"))):(s&&s.data&&alert(s.data),console.log({record:r,action:a,response:s})),e("table.records").unblock()})}),e("form").submit(function(s){var t;return t=e("#wc_square_enable_sandbox").is(":checked")?"sandbox":"production",e(this).append(e("<input>",{type:"hidden",name:"wc_square_environment",value:t}))}),s=function(t){var a,n;return(a=e("span.progress"))&&0!==a.length||(e("p.sync-result").append(' <span class="progress" style="display:block"></span>'),a=e("span.progress")),n={action:"wc_square_get_sync_with_square_status",security:wc_square_admin_settings.get_sync_with_square_status_nonce,job_id:t},e.post(wc_square_admin_settings.ajax_url,n,function(t){var n,r;if(t&&t.data)return t.success&&t.data.id?(e("table.sync .spinner").css("visibility","visible"),e("#wc-square_clear-sync-records").prop("disabled",!0),e("table.records .actions button").prop("disabled",!0),"completed"!==(r=t.data.status)&&"failed"!==r?(n=" ","product_import"===t.data.action?(n+=wc_square_admin_settings.i18n.skipped+": "+parseInt(t.data.skipped_products_count,10)+"<br/>",n+=wc_square_admin_settings.i18n.updated+": "+parseInt(t.data.updated_products_count,10)+"<br/>",n+=wc_square_admin_settings.i18n.imported+": "+parseInt(t.data.imported_products_count,10)):t.data.percentage&&(n+=parseInt(t.data.percentage,10)+"%"),a.html(n),setTimeout(function(){return s(t.data.id)},3e4)):location.reload()):(e("#wc-square_clear-sync-records").prop("disabled",!1),e("table.records .actions button").prop("disabled",!1),e("table.sync .spinner").css("visibility","hidden"),console.log(t))})},wc_square_admin_settings.existing_sync_job_id?s(wc_square_admin_settings.existing_sync_job_id):void 0})}).call(void 0);
2
  //# sourceMappingURL=wc-square-admin-settings.min.js.map
assets/js/admin/wc-square-admin-settings.min.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["wc-square-admin-settings.min.js"],"names":["jQuery","document","ready","$","_getSyncStatus","noRecordsFoundRow","ref","ref1","window","typenow","pagenow","change","$inventory_sync","$inventory_sync_row","system_of_record","this","val","closest","next","html","wc_square_admin_settings","i18n","sync_inventory_label","find","sync_inventory_description","show","prop","hide","on","e","preventDefault","WCBackboneModal","View","target","trigger","data","unbind","action","dispatch","sync_in_background","security","import_products_from_square","post","ajax_url","response","message","success","alert","location","reload","block","overlayCSS","opacity","sync_products_with_square","unblock","console","log","no_records_found","id","handle","handle_sync_with_square_records","recordId","rowId","remove","length","resolved","record","job_id","$progress","append","get_sync_with_square_status_nonce","progress","ref2","css","status","skipped","parseInt","skipped_products_count","imported","processed_products_count","percentage","setTimeout","existing_sync_job_id","call","undefined"],"mappings":"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","file":"wc-square-admin-settings.min.js"}
1
+ {"version":3,"sources":["wc-square-admin-settings.min.js"],"names":["jQuery","document","ready","$","_getSyncStatus","noRecordsFoundRow","ref","ref1","window","typenow","pagenow","wc_square_admin_settings","is_sandbox","hide","next","closest","change","$inventory_sync","$inventory_sync_row","system_of_record","this","val","html","i18n","sync_inventory_label","find","sync_inventory_description","show","prop","on","e","preventDefault","WCBackboneModal","View","target","trigger","body","data","block","message","overlayCSS","opacity","onBlock","css","position","action","dispatch","sync_in_background","security","import_products_from_square","post","ajax_url","response","alert","location","reload","sync_products_with_square","success","unblock","no_records_found","id","handle","handle_sync_with_square_records","console","log","recordId","rowId","remove","length","resolved","record","submit","environment","is","append","type","name","value","job_id","$progress","get_sync_with_square_status_nonce","progress","ref2","status","skipped","parseInt","skipped_products_count","updated","updated_products_count","imported","imported_products_count","percentage","setTimeout","existing_sync_job_id","call","undefined"],"mappings":"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","file":"wc-square-admin-settings.min.js"}
assets/js/frontend/wc-square.coffee CHANGED
@@ -37,6 +37,8 @@ jQuery( document ).ready ( $ ) ->
37
  @is_user_logged_in = args.is_user_logged_in
38
  @location_id = args.location_id
39
  @logging_enabled = args.logging_enabled
 
 
40
 
41
  # which payment form?
42
  if $( 'form.checkout' ).length
@@ -397,6 +399,9 @@ jQuery( document ).ready ( $ ) ->
397
  handle_verify_buyer_response: ( errors, verification_result ) =>
398
 
399
  if errors
 
 
 
400
  return this.handle_errors( errors )
401
 
402
  # no errors, but also no verification token
@@ -502,13 +507,12 @@ jQuery( document ).ready ( $ ) ->
502
 
503
  if errors
504
 
505
- field_order = [ "cardNumber", "expirationDate", "cvv", "postalCode" ]
506
-
507
- # sort based on the field order
508
- # without the brackets around a.field and b.field the precedence is different and gives different results
509
- errors.sort (a,b) -> field_order.indexOf(a.field) - field_order.indexOf(b.field)
510
 
511
- console.error errors
 
 
 
512
 
513
  $( errors ).each ( index, error ) =>
514
 
@@ -526,7 +530,11 @@ jQuery( document ).ready ( $ ) ->
526
  if messages.length is 0
527
  messages.push( @general_error )
528
 
529
- this.render_errors( messages )
 
 
 
 
530
 
531
  this.unblock_ui()
532
 
@@ -627,3 +635,44 @@ jQuery( document ).ready ( $ ) ->
627
  console.error 'Square Error: ' + message
628
  else
629
  console.log 'Square: ' + message
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  @is_user_logged_in = args.is_user_logged_in
38
  @location_id = args.location_id
39
  @logging_enabled = args.logging_enabled
40
+ @ajax_wc_checkout_validate_nonce = args.ajax_wc_checkout_validate_nonce
41
+ @is_manual_order_payment = args.is_manual_order_payment
42
 
43
  # which payment form?
44
  if $( 'form.checkout' ).length
399
  handle_verify_buyer_response: ( errors, verification_result ) =>
400
 
401
  if errors
402
+ $( errors ).each ( index, error ) =>
403
+ if not error.field
404
+ error.field = 'none';
405
  return this.handle_errors( errors )
406
 
407
  # no errors, but also no verification token
507
 
508
  if errors
509
 
510
+ field_order = [ "none", "cardNumber", "expirationDate", "cvv", "postalCode" ]
 
 
 
 
511
 
512
+ if errors.length >= 1
513
+ # sort based on the field order
514
+ # without the brackets around a.field and b.field the precedence is different and gives different results
515
+ errors.sort (a,b) -> field_order.indexOf(a.field) - field_order.indexOf(b.field)
516
 
517
  $( errors ).each ( index, error ) =>
518
 
530
  if messages.length is 0
531
  messages.push( @general_error )
532
 
533
+ # Conditionally process error rendering
534
+ if not @is_add_payment_method_page and not @is_manual_order_payment
535
+ this.render_checkout_errors( messages )
536
+ else
537
+ this.render_errors( messages )
538
 
539
  this.unblock_ui()
540
 
635
  console.error 'Square Error: ' + message
636
  else
637
  console.log 'Square: ' + message
638
+
639
+ # AJAX validate WooCommerce form data.
640
+ #
641
+ # Triggered only if errors are present on Square payment form.
642
+ #
643
+ # @since 2.2
644
+ #
645
+ # @param Array square_errors Square validation errors.
646
+ render_checkout_errors: ( square_errors ) ->
647
+ ajax_url = wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', @id + '_checkout_handler' )
648
+ form_data = @form.serializeArray()
649
+ square_handler = this
650
+
651
+ # Add action field to data for nonce verification.
652
+ form_data.push({
653
+ name: 'wc_' + @id + '_checkout_validate_nonce', 'value': @ajax_wc_checkout_validate_nonce
654
+ })
655
+
656
+ $.ajax({
657
+ url : ajax_url,
658
+ method : 'post',
659
+ cache : false,
660
+ data : form_data,
661
+ complete : ( response ) ->
662
+ result = response.responseJSON
663
+
664
+ # If validation is not triggered and WooCommerce returns failure.
665
+ # Temporary workaround to fix problems when user email is invalid.
666
+ if result.hasOwnProperty( 'result' ) and 'failure' == result.result
667
+ $(result.messages).map ->
668
+ errors = []
669
+ $( this ).children( 'li' ).each ->
670
+ errors.push( $( this ).text().trim() )
671
+ square_errors.unshift( ...errors )
672
+
673
+ # If validation is complete and WooCommerce returns validaiton errors.
674
+ else if result.hasOwnProperty( 'success' ) and not result.success
675
+ square_errors.unshift( ...result.data.messages )
676
+
677
+ square_handler.render_errors( square_errors )
678
+ })
assets/js/frontend/wc-square.min.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();(function(){var e=[].indexOf;jQuery(document).ready(function(t){return window.WC_Square_Payment_Form_Handler=function(){function n(e){if(_classCallCheck(this,n),this.set_payment_fields=this.set_payment_fields.bind(this),this.get_form_params=this.get_form_params.bind(this),this.handle_input_event=this.handle_input_event.bind(this),this.handle_verify_buyer_response=this.handle_verify_buyer_response.bind(this),this.id=e.id,this.id_dasherized=e.id_dasherized,this.csc_required=e.csc_required,this.enabled_card_types=e.enabled_card_types,this.square_card_types=e.square_card_types,this.ajax_log_nonce=e.ajax_log_nonce,this.ajax_url=e.ajax_url,this.application_id=e.application_id,this.currency_code=e.currency_code,this.general_error=e.general_error,this.input_styles=e.input_styles,this.is_3ds_enabled=e.is_3d_secure_enabled,this.is_add_payment_method_page=e.is_add_payment_method_page,this.is_checkout_registration_enabled=e.is_checkout_registration_enabled,this.is_user_logged_in=e.is_user_logged_in,this.location_id=e.location_id,this.logging_enabled=e.logging_enabled,t("form.checkout").length)this.form=t("form.checkout"),this.handle_checkout_page();else if(t("form#order_review").length)this.form=t("form#order_review"),this.handle_pay_page();else{if(!t("form#add_payment_method").length)return void this.log("No payment form found!");this.form=t("form#add_payment_method"),this.handle_add_payment_method_page()}this.params=window.sv_wc_payment_gateway_payment_form_params,t(document.body).on("checkout_error",function(){return t("input[name=wc-square-credit-card-payment-nonce]").val(""),t("input[name=wc-square-credit-card-buyer-verification-token]").val("")}),t(document.body).on("click","#payment_method_"+this.id,function(){if(this.payment_form)return this.log("Recalculating payment form size"),this.payment_form.recalculateSize()})}return _createClass(n,[{key:"handle_checkout_page",value:function(){var e=this;return t(document.body).on("updated_checkout",function(){return e.set_payment_fields()}),t(document.body).on("updated_checkout",function(){return e.handle_saved_payment_methods()}),this.form.on("checkout_place_order_"+this.id,function(){return e.validate_payment_data()})}},{key:"handle_saved_payment_methods",value:function(){var e,n,i;if(i=this.id_dasherized,n=this,e=t("div.js-wc-"+i+"-new-payment-method-form"),t("input.js-wc-"+this.id_dasherized+"-payment-token").change(function(){return t("input.js-wc-"+i+"-payment-token:checked").val()?e.slideUp(200):e.slideDown(200)}).change(),t("input#createaccount").change(function(){return t(this).is(":checked")?n.show_save_payment_checkbox(i):n.hide_save_payment_checkbox(i)}),t("input#createaccount").is(":checked")||t("input#createaccount").change(),!this.is_user_logged_in&&!this.is_checkout_registration_enabled)return this.hide_save_payment_checkbox(i)}},{key:"handle_pay_page",value:function(){var e=this;return this.set_payment_fields(),this.handle_saved_payment_methods(),this.form.submit(function(){if(t("#order_review input[name=payment_method]:checked").val()===e.id)return e.validate_payment_data()})}},{key:"handle_add_payment_method_page",value:function(){var e=this;return this.set_payment_fields(),this.form.submit(function(){if(t("#add_payment_method input[name=payment_method]:checked").val()===e.id)return e.validate_payment_data()})}},{key:"set_payment_fields",value:function(){var e,n,i;if(t("#wc-"+this.id_dasherized+"-account-number-hosted").is("iframe")){this.log("Re-adding payment form"),i=this.form_fields;for(e in i)n=i[e],t(n.attr("id")).replaceWith(n);return this.handle_form_loaded()}return this.payment_form&&(this.log("Destroying payment form"),this.payment_form.destroy()),this.log("Building payment form"),this.payment_form=new SqPaymentForm(this.get_form_params()),this.payment_form.build()}},{key:"get_form_params",value:function(){var e=this;return this.form_fields={card_number:t("#wc-"+this.id_dasherized+"-account-number-hosted"),expiration:t("#wc-"+this.id_dasherized+"-expiry-hosted"),csc:t("#wc-"+this.id_dasherized+"-csc-hosted"),postal_code:t("#wc-"+this.id_dasherized+"-postal-code-hosted")},{applicationId:this.application_id,locationId:this.location_id,cardNumber:{elementId:this.form_fields.card_number.attr("id"),placeholder:this.form_fields.card_number.data("placeholder")},expirationDate:{elementId:this.form_fields.expiration.attr("id"),placeholder:this.form_fields.expiration.data("placeholder")},cvv:{elementId:this.form_fields.csc.attr("id"),placeholder:this.form_fields.csc.data("placeholder")},postalCode:{elementId:this.form_fields.postal_code.attr("id"),placeholder:this.form_fields.postal_code.data("placeholder")},inputClass:"wc-"+this.id_dasherized+"-payment-field",inputStyles:this.input_styles,callbacks:{inputEventReceived:function(t){return e.handle_input_event(t)},cardNonceResponseReceived:function(t,n,i){return e.handle_card_nonce_response(t,n,i)},unsupportedBrowserDetected:function(){return e.handle_unsupported_browser()},paymentFormLoaded:function(){return e.handle_form_loaded()}}}}},{key:"handle_form_loaded",value:function(){if(this.log("Payment form loaded"),this.payment_form.setPostalCode(t("#billing_postcode").val()),t("form.checkout").length||t("#billing_postcode").val())return t(".wc-square-credit-card-card-postal-code-parent").addClass("hidden")}},{key:"handle_input_event",value:function(e){var n;if(n=t("#"+e.elementId),"cardBrandChanged"===e.eventType)return this.handle_card_brand_change(e.cardBrand,n)}},{key:"handle_card_brand_change",value:function(n,i){var a;return this.log("Card brand changed to "+n),i.attr("class",function(e,t){return t.replace(/(^|\s)card-type-\S+/g,"")}),a="plain",null!=n&&"unknown"!==n||(n=""),null!=this.square_card_types[n]&&(n=this.square_card_types[n]),a=n&&e.call(this.enabled_card_types,n)<0?"invalid":n,t("input[name=wc-"+this.id_dasherized+"-card-type]").val(n),i.addClass("card-type-"+a)}},{key:"validate_payment_data",value:function(){var e;return!this.form.is(".processing")&&(this.has_nonce()?(this.log("Payment nonce present, placing order"),!0):(e=this.get_tokenized_payment_method_id())?!this.is_3ds_enabled||(this.has_verification_token()?(this.log("Tokenized payment verification token present, placing order"),!0):(this.log("Requesting verification token for tokenized payment"),this.block_ui(),this.payment_form.verifyBuyer(e,this.get_verification_details(),this.handle_verify_buyer_response),!1)):(this.log("Requesting payment nonce"),this.block_ui(),this.payment_form.requestCardNonce(),!1))}},{key:"get_tokenized_payment_method_id",value:function(){return t(".payment_method_"+this.id).find(".js-wc-square-credit-card-payment-token:checked").val()}},{key:"handle_card_nonce_response",value:function(e,n,i){var a;return e?this.handle_errors(e):n?(this.log("Card data received"),this.log(i),this.log_data(i,"response"),i.last_4&&t("input[name=wc-"+this.id_dasherized+"-last-four]").val(i.last_4),i.exp_month&&t("input[name=wc-"+this.id_dasherized+"-exp-month]").val(i.exp_month),i.exp_year&&t("input[name=wc-"+this.id_dasherized+"-exp-year]").val(i.exp_year),i.billing_postal_code&&t("input[name=wc-square-credit-card-payment-postcode]").val(i.billing_postal_code),t("input[name=wc-"+this.id_dasherized+"-payment-nonce]").val(n),this.is_3ds_enabled?(this.log("Verifying buyer"),void this.payment_form.verifyBuyer(n,this.get_verification_details(),this.handle_verify_buyer_response)):this.form.submit()):(a="Nonce is missing from the Square response",this.log(a,"error"),this.log_data(a,"response"),this.handle_errors())}},{key:"handle_verify_buyer_response",value:function(e,n){var i;return e?this.handle_errors(e):n&&n.token?(this.log("Verification result received"),this.log(n),t("input[name=wc-"+this.id_dasherized+"-buyer-verification-token]").val(n.token),this.form.submit()):(i="Verification token is missing from the Square response",this.log(i,"error"),this.log_data(i,"response"),this.handle_errors())}},{key:"get_verification_details",value:function(){var e,n,i,a,r,o,s,d,l,_,c;return"CHARGE"===(c={billingContact:{familyName:null!=(e=t("#billing_last_name").val())?e:"",givenName:null!=(n=t("#billing_first_name").val())?n:"",email:null!=(i=t("#billing_email").val())?i:"",country:null!=(a=t("#billing_country").val())?a:"",region:null!=(r=t("#billing_state").val())?r:"",city:null!=(o=t("#billing_city").val())?o:"",postalCode:null!=(s=t("#billing_postcode").val())?s:"",phone:null!=(d=t("#billing_phone").val())?d:"",addressLines:[null!=(l=t("#billing_address_1").val())?l:"",null!=(_=t("#billing_address_2").val())?_:""]},intent:this.get_intent()}).intent&&(c.amount=this.get_amount(),c.currencyCode=this.currency_code),this.log(c),c}},{key:"get_intent",value:function(){var e,n;return e=t("#wc-square-credit-card-tokenize-payment-method"),n=e.is("input:checkbox")?e.is(":checked"):"true"===e.val(),!this.get_tokenized_payment_method_id()&&n?"STORE":"CHARGE"}},{key:"get_amount",value:function(){return t("input[name=wc-"+this.id_dasherized+"-amount]").val()}},{key:"handle_unsupported_browser",value:function(){}},{key:"handle_errors",value:function(){var e,n,i=this,a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.log("Error getting payment data","error"),t("input[name=wc-square-credit-card-payment-nonce]").val(""),t("input[name=wc-square-credit-card-buyer-verification-token]").val(""),n=[],a&&(e=["cardNumber","expirationDate","cvv","postalCode"],a.sort(function(t,n){return e.indexOf(t.field)-e.indexOf(n.field)}),console.error(a),t(a).each(function(e,t){var r;return"UNSUPPORTED_CARD_BRAND"===(r=t.type)||"VALIDATION_ERROR"===r?n.push(t.message.replace(/CVV/,"CSC")):i.log_data(a,"response")})),0===n.length&&n.push(this.general_error),this.render_errors(n),this.unblock_ui()}},{key:"render_errors",value:function(e){return t(".woocommerce-error, .woocommerce-message").remove(),this.form.prepend('<ul class="woocommerce-error"><li>'+e.join("</li><li>")+"</li></ul>"),this.form.removeClass("processing").unblock(),this.form.find(".input-text, select").blur(),t("html, body").animate({scrollTop:this.form.offset().top-100},1e3)}},{key:"block_ui",value:function(){return this.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})}},{key:"unblock_ui",value:function(){return this.form.unblock()}},{key:"hide_save_payment_checkbox",value:function(e){var n;return(n=t("input.js-wc-"+e+"-tokenize-payment-method").closest("p.form-row")).hide(),n.next().hide()}},{key:"show_save_payment_checkbox",value:function(e){var n;return(n=t("input.js-wc-"+e+"-tokenize-payment-method").closest("p.form-row")).slideDown(),n.next().show()}},{key:"has_nonce",value:function(){return t("input[name=wc-"+this.id_dasherized+"-payment-nonce]").val()}},{key:"has_verification_token",value:function(){return t("input[name=wc-"+this.id_dasherized+"-buyer-verification-token]").val()}},{key:"log_data",value:function(e,n){var i;if(this.logging_enabled)return i={action:"wc_"+this.id+"_log_js_data",security:this.ajax_log_nonce,type:n,data:e},t.ajax({url:this.ajax_url,data:i})}},{key:"log",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"notice";if(this.logging_enabled)return"error"===t?console.error("Square Error: "+e):console.log("Square: "+e)}}]),n}()})}).call(void 0);
2
  //# sourceMappingURL=wc-square.min.js.map
1
+ "use strict";function _toConsumableArray(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();(function(){var e=[].indexOf;jQuery(document).ready(function(t){return window.WC_Square_Payment_Form_Handler=function(){function n(e){if(_classCallCheck(this,n),this.set_payment_fields=this.set_payment_fields.bind(this),this.get_form_params=this.get_form_params.bind(this),this.handle_input_event=this.handle_input_event.bind(this),this.handle_verify_buyer_response=this.handle_verify_buyer_response.bind(this),this.id=e.id,this.id_dasherized=e.id_dasherized,this.csc_required=e.csc_required,this.enabled_card_types=e.enabled_card_types,this.square_card_types=e.square_card_types,this.ajax_log_nonce=e.ajax_log_nonce,this.ajax_url=e.ajax_url,this.application_id=e.application_id,this.currency_code=e.currency_code,this.general_error=e.general_error,this.input_styles=e.input_styles,this.is_3ds_enabled=e.is_3d_secure_enabled,this.is_add_payment_method_page=e.is_add_payment_method_page,this.is_checkout_registration_enabled=e.is_checkout_registration_enabled,this.is_user_logged_in=e.is_user_logged_in,this.location_id=e.location_id,this.logging_enabled=e.logging_enabled,this.ajax_wc_checkout_validate_nonce=e.ajax_wc_checkout_validate_nonce,this.is_manual_order_payment=e.is_manual_order_payment,t("form.checkout").length)this.form=t("form.checkout"),this.handle_checkout_page();else if(t("form#order_review").length)this.form=t("form#order_review"),this.handle_pay_page();else{if(!t("form#add_payment_method").length)return void this.log("No payment form found!");this.form=t("form#add_payment_method"),this.handle_add_payment_method_page()}this.params=window.sv_wc_payment_gateway_payment_form_params,t(document.body).on("checkout_error",function(){return t("input[name=wc-square-credit-card-payment-nonce]").val(""),t("input[name=wc-square-credit-card-buyer-verification-token]").val("")}),t(document.body).on("click","#payment_method_"+this.id,function(){if(this.payment_form)return this.log("Recalculating payment form size"),this.payment_form.recalculateSize()})}return _createClass(n,[{key:"handle_checkout_page",value:function(){var e=this;return t(document.body).on("updated_checkout",function(){return e.set_payment_fields()}),t(document.body).on("updated_checkout",function(){return e.handle_saved_payment_methods()}),this.form.on("checkout_place_order_"+this.id,function(){return e.validate_payment_data()})}},{key:"handle_saved_payment_methods",value:function(){var e,n,i;if(i=this.id_dasherized,n=this,e=t("div.js-wc-"+i+"-new-payment-method-form"),t("input.js-wc-"+this.id_dasherized+"-payment-token").change(function(){return t("input.js-wc-"+i+"-payment-token:checked").val()?e.slideUp(200):e.slideDown(200)}).change(),t("input#createaccount").change(function(){return t(this).is(":checked")?n.show_save_payment_checkbox(i):n.hide_save_payment_checkbox(i)}),t("input#createaccount").is(":checked")||t("input#createaccount").change(),!this.is_user_logged_in&&!this.is_checkout_registration_enabled)return this.hide_save_payment_checkbox(i)}},{key:"handle_pay_page",value:function(){var e=this;return this.set_payment_fields(),this.handle_saved_payment_methods(),this.form.submit(function(){if(t("#order_review input[name=payment_method]:checked").val()===e.id)return e.validate_payment_data()})}},{key:"handle_add_payment_method_page",value:function(){var e=this;return this.set_payment_fields(),this.form.submit(function(){if(t("#add_payment_method input[name=payment_method]:checked").val()===e.id)return e.validate_payment_data()})}},{key:"set_payment_fields",value:function(){var e,n,i;if(t("#wc-"+this.id_dasherized+"-account-number-hosted").is("iframe")){this.log("Re-adding payment form"),i=this.form_fields;for(e in i)n=i[e],t(n.attr("id")).replaceWith(n);return this.handle_form_loaded()}return this.payment_form&&(this.log("Destroying payment form"),this.payment_form.destroy()),this.log("Building payment form"),this.payment_form=new SqPaymentForm(this.get_form_params()),this.payment_form.build()}},{key:"get_form_params",value:function(){var e=this;return this.form_fields={card_number:t("#wc-"+this.id_dasherized+"-account-number-hosted"),expiration:t("#wc-"+this.id_dasherized+"-expiry-hosted"),csc:t("#wc-"+this.id_dasherized+"-csc-hosted"),postal_code:t("#wc-"+this.id_dasherized+"-postal-code-hosted")},{applicationId:this.application_id,locationId:this.location_id,cardNumber:{elementId:this.form_fields.card_number.attr("id"),placeholder:this.form_fields.card_number.data("placeholder")},expirationDate:{elementId:this.form_fields.expiration.attr("id"),placeholder:this.form_fields.expiration.data("placeholder")},cvv:{elementId:this.form_fields.csc.attr("id"),placeholder:this.form_fields.csc.data("placeholder")},postalCode:{elementId:this.form_fields.postal_code.attr("id"),placeholder:this.form_fields.postal_code.data("placeholder")},inputClass:"wc-"+this.id_dasherized+"-payment-field",inputStyles:this.input_styles,callbacks:{inputEventReceived:function(t){return e.handle_input_event(t)},cardNonceResponseReceived:function(t,n,i){return e.handle_card_nonce_response(t,n,i)},unsupportedBrowserDetected:function(){return e.handle_unsupported_browser()},paymentFormLoaded:function(){return e.handle_form_loaded()}}}}},{key:"handle_form_loaded",value:function(){if(this.log("Payment form loaded"),this.payment_form.setPostalCode(t("#billing_postcode").val()),t("form.checkout").length||t("#billing_postcode").val())return t(".wc-square-credit-card-card-postal-code-parent").addClass("hidden")}},{key:"handle_input_event",value:function(e){var n;if(n=t("#"+e.elementId),"cardBrandChanged"===e.eventType)return this.handle_card_brand_change(e.cardBrand,n)}},{key:"handle_card_brand_change",value:function(n,i){var a;return this.log("Card brand changed to "+n),i.attr("class",function(e,t){return t.replace(/(^|\s)card-type-\S+/g,"")}),a="plain",null!=n&&"unknown"!==n||(n=""),null!=this.square_card_types[n]&&(n=this.square_card_types[n]),a=n&&e.call(this.enabled_card_types,n)<0?"invalid":n,t("input[name=wc-"+this.id_dasherized+"-card-type]").val(n),i.addClass("card-type-"+a)}},{key:"validate_payment_data",value:function(){var e;return!this.form.is(".processing")&&(this.has_nonce()?(this.log("Payment nonce present, placing order"),!0):(e=this.get_tokenized_payment_method_id())?!this.is_3ds_enabled||(this.has_verification_token()?(this.log("Tokenized payment verification token present, placing order"),!0):(this.log("Requesting verification token for tokenized payment"),this.block_ui(),this.payment_form.verifyBuyer(e,this.get_verification_details(),this.handle_verify_buyer_response),!1)):(this.log("Requesting payment nonce"),this.block_ui(),this.payment_form.requestCardNonce(),!1))}},{key:"get_tokenized_payment_method_id",value:function(){return t(".payment_method_"+this.id).find(".js-wc-square-credit-card-payment-token:checked").val()}},{key:"handle_card_nonce_response",value:function(e,n,i){var a;return e?this.handle_errors(e):n?(this.log("Card data received"),this.log(i),this.log_data(i,"response"),i.last_4&&t("input[name=wc-"+this.id_dasherized+"-last-four]").val(i.last_4),i.exp_month&&t("input[name=wc-"+this.id_dasherized+"-exp-month]").val(i.exp_month),i.exp_year&&t("input[name=wc-"+this.id_dasherized+"-exp-year]").val(i.exp_year),i.billing_postal_code&&t("input[name=wc-square-credit-card-payment-postcode]").val(i.billing_postal_code),t("input[name=wc-"+this.id_dasherized+"-payment-nonce]").val(n),this.is_3ds_enabled?(this.log("Verifying buyer"),void this.payment_form.verifyBuyer(n,this.get_verification_details(),this.handle_verify_buyer_response)):this.form.submit()):(a="Nonce is missing from the Square response",this.log(a,"error"),this.log_data(a,"response"),this.handle_errors())}},{key:"handle_verify_buyer_response",value:function(e,n){var i;return e?(t(e).each(function(e,t){if(!t.field)return t.field="none"}),this.handle_errors(e)):n&&n.token?(this.log("Verification result received"),this.log(n),t("input[name=wc-"+this.id_dasherized+"-buyer-verification-token]").val(n.token),this.form.submit()):(i="Verification token is missing from the Square response",this.log(i,"error"),this.log_data(i,"response"),this.handle_errors())}},{key:"get_verification_details",value:function(){var e,n,i,a,r,o,s,d,l,_,c;return"CHARGE"===(c={billingContact:{familyName:null!=(e=t("#billing_last_name").val())?e:"",givenName:null!=(n=t("#billing_first_name").val())?n:"",email:null!=(i=t("#billing_email").val())?i:"",country:null!=(a=t("#billing_country").val())?a:"",region:null!=(r=t("#billing_state").val())?r:"",city:null!=(o=t("#billing_city").val())?o:"",postalCode:null!=(s=t("#billing_postcode").val())?s:"",phone:null!=(d=t("#billing_phone").val())?d:"",addressLines:[null!=(l=t("#billing_address_1").val())?l:"",null!=(_=t("#billing_address_2").val())?_:""]},intent:this.get_intent()}).intent&&(c.amount=this.get_amount(),c.currencyCode=this.currency_code),this.log(c),c}},{key:"get_intent",value:function(){var e,n;return e=t("#wc-square-credit-card-tokenize-payment-method"),n=e.is("input:checkbox")?e.is(":checked"):"true"===e.val(),!this.get_tokenized_payment_method_id()&&n?"STORE":"CHARGE"}},{key:"get_amount",value:function(){return t("input[name=wc-"+this.id_dasherized+"-amount]").val()}},{key:"handle_unsupported_browser",value:function(){}},{key:"handle_errors",value:function(){var e,n,i=this,a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.log("Error getting payment data","error"),t("input[name=wc-square-credit-card-payment-nonce]").val(""),t("input[name=wc-square-credit-card-buyer-verification-token]").val(""),n=[],a&&(e=["none","cardNumber","expirationDate","cvv","postalCode"],a.length>=1&&a.sort(function(t,n){return e.indexOf(t.field)-e.indexOf(n.field)}),t(a).each(function(e,t){var r;return"UNSUPPORTED_CARD_BRAND"===(r=t.type)||"VALIDATION_ERROR"===r?n.push(t.message.replace(/CVV/,"CSC")):i.log_data(a,"response")})),0===n.length&&n.push(this.general_error),this.is_add_payment_method_page||this.is_manual_order_payment?this.render_errors(n):this.render_checkout_errors(n),this.unblock_ui()}},{key:"render_errors",value:function(e){return t(".woocommerce-error, .woocommerce-message").remove(),this.form.prepend('<ul class="woocommerce-error"><li>'+e.join("</li><li>")+"</li></ul>"),this.form.removeClass("processing").unblock(),this.form.find(".input-text, select").blur(),t("html, body").animate({scrollTop:this.form.offset().top-100},1e3)}},{key:"block_ui",value:function(){return this.form.block({message:null,overlayCSS:{background:"#fff",opacity:.6}})}},{key:"unblock_ui",value:function(){return this.form.unblock()}},{key:"hide_save_payment_checkbox",value:function(e){var n;return(n=t("input.js-wc-"+e+"-tokenize-payment-method").closest("p.form-row")).hide(),n.next().hide()}},{key:"show_save_payment_checkbox",value:function(e){var n;return(n=t("input.js-wc-"+e+"-tokenize-payment-method").closest("p.form-row")).slideDown(),n.next().show()}},{key:"has_nonce",value:function(){return t("input[name=wc-"+this.id_dasherized+"-payment-nonce]").val()}},{key:"has_verification_token",value:function(){return t("input[name=wc-"+this.id_dasherized+"-buyer-verification-token]").val()}},{key:"log_data",value:function(e,n){var i;if(this.logging_enabled)return i={action:"wc_"+this.id+"_log_js_data",security:this.ajax_log_nonce,type:n,data:e},t.ajax({url:this.ajax_url,data:i})}},{key:"log",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"notice";if(this.logging_enabled)return"error"===t?console.error("Square Error: "+e):console.log("Square: "+e)}},{key:"render_checkout_errors",value:function(e){var n,i,a;return n=wc_cart_fragments_params.wc_ajax_url.toString().replace("%%endpoint%%",this.id+"_checkout_handler"),i=this.form.serializeArray(),a=this,i.push({name:"wc_"+this.id+"_checkout_validate_nonce",value:this.ajax_wc_checkout_validate_nonce}),t.ajax({url:n,method:"post",cache:!1,data:i,complete:function(n){var i;return(i=n.responseJSON).hasOwnProperty("result")&&"failure"===i.result?t(i.messages).map(function(){var n;return n=[],t(this).children("li").each(function(){return n.push(t(this).text().trim())}),e.unshift.apply(e,_toConsumableArray(n))}):i.hasOwnProperty("success")&&!i.success&&e.unshift.apply(e,_toConsumableArray(i.data.messages)),a.render_errors(e)}})}}]),n}()})}).call(void 0);
2
  //# sourceMappingURL=wc-square.min.js.map
assets/js/frontend/wc-square.min.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["wc-square.min.js"],"names":["_classCallCheck","instance","Constructor","TypeError","_createClass","defineProperties","target","props","i","length","descriptor","enumerable","configurable","writable","Object","defineProperty","key","protoProps","staticProps","prototype","indexOf","jQuery","document","ready","$","window","WC_Square_Payment_Form_Handler","args","this","set_payment_fields","bind","get_form_params","handle_input_event","handle_verify_buyer_response","id","id_dasherized","csc_required","enabled_card_types","square_card_types","ajax_log_nonce","ajax_url","application_id","currency_code","general_error","input_styles","is_3ds_enabled","is_3d_secure_enabled","is_add_payment_method_page","is_checkout_registration_enabled","is_user_logged_in","location_id","logging_enabled","form","handle_checkout_page","handle_pay_page","log","handle_add_payment_method_page","params","body","on","val","payment_form","recalculateSize","value","_this","handle_saved_payment_methods","validate_payment_data","$new_payment_method_selection","form_handler","change","slideUp","slideDown","is","show_save_payment_checkbox","hide_save_payment_checkbox","_this2","submit","_this3","_","field","ref","form_fields","attr","replaceWith","handle_form_loaded","destroy","SqPaymentForm","build","_this4","card_number","expiration","csc","postal_code","applicationId","locationId","cardNumber","elementId","placeholder","data","expirationDate","cvv","postalCode","inputClass","inputStyles","callbacks","inputEventReceived","event","cardNonceResponseReceived","errors","nonce","cardData","handle_card_nonce_response","unsupportedBrowserDetected","handle_unsupported_browser","paymentFormLoaded","setPostalCode","addClass","$input","eventType","handle_card_brand_change","cardBrand","brand","card_class","c","replace","call","tokenized_card_id","has_nonce","get_tokenized_payment_method_id","has_verification_token","block_ui","verifyBuyer","get_verification_details","requestCardNonce","find","message","handle_errors","log_data","last_4","exp_month","exp_year","billing_postal_code","verification_result","token","ref1","ref2","ref3","ref4","ref5","ref6","ref7","ref8","ref9","verification_details","billingContact","familyName","givenName","email","country","region","city","phone","addressLines","intent","get_intent","amount","get_amount","currencyCode","$save_method_input","save_payment_method","field_order","messages","_this5","arguments","undefined","sort","a","b","console","error","each","index","type","push","render_errors","unblock_ui","remove","prepend","join","removeClass","unblock","blur","animate","scrollTop","offset","top","block","overlayCSS","background","opacity","$parent_row","closest","hide","next","show","ajax_data","action","security","ajax","url"],"mappings":"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","file":"wc-square.min.js"}
1
+ {"version":3,"sources":["wc-square.min.js"],"names":["_toConsumableArray","arr","Array","isArray","i","arr2","length","from","_classCallCheck","instance","Constructor","TypeError","_createClass","defineProperties","target","props","descriptor","enumerable","configurable","writable","Object","defineProperty","key","protoProps","staticProps","prototype","indexOf","jQuery","document","ready","$","window","WC_Square_Payment_Form_Handler","args","this","set_payment_fields","bind","get_form_params","handle_input_event","handle_verify_buyer_response","id","id_dasherized","csc_required","enabled_card_types","square_card_types","ajax_log_nonce","ajax_url","application_id","currency_code","general_error","input_styles","is_3ds_enabled","is_3d_secure_enabled","is_add_payment_method_page","is_checkout_registration_enabled","is_user_logged_in","location_id","logging_enabled","ajax_wc_checkout_validate_nonce","is_manual_order_payment","form","handle_checkout_page","handle_pay_page","log","handle_add_payment_method_page","params","body","on","val","payment_form","recalculateSize","value","_this","handle_saved_payment_methods","validate_payment_data","$new_payment_method_selection","form_handler","change","slideUp","slideDown","is","show_save_payment_checkbox","hide_save_payment_checkbox","_this2","submit","_this3","_","field","ref","form_fields","attr","replaceWith","handle_form_loaded","destroy","SqPaymentForm","build","_this4","card_number","expiration","csc","postal_code","applicationId","locationId","cardNumber","elementId","placeholder","data","expirationDate","cvv","postalCode","inputClass","inputStyles","callbacks","inputEventReceived","event","cardNonceResponseReceived","errors","nonce","cardData","handle_card_nonce_response","unsupportedBrowserDetected","handle_unsupported_browser","paymentFormLoaded","setPostalCode","addClass","$input","eventType","handle_card_brand_change","cardBrand","brand","card_class","c","replace","call","tokenized_card_id","has_nonce","get_tokenized_payment_method_id","has_verification_token","block_ui","verifyBuyer","get_verification_details","requestCardNonce","find","message","handle_errors","log_data","last_4","exp_month","exp_year","billing_postal_code","verification_result","each","index","error","token","ref1","ref2","ref3","ref4","ref5","ref6","ref7","ref8","ref9","verification_details","billingContact","familyName","givenName","email","country","region","city","phone","addressLines","intent","get_intent","amount","get_amount","currencyCode","$save_method_input","save_payment_method","field_order","messages","_this5","arguments","undefined","sort","a","b","type","push","render_errors","render_checkout_errors","unblock_ui","remove","prepend","join","removeClass","unblock","blur","animate","scrollTop","offset","top","block","overlayCSS","background","opacity","$parent_row","closest","hide","next","show","ajax_data","action","security","ajax","url","console","square_errors","form_data","square_handler","wc_cart_fragments_params","wc_ajax_url","toString","serializeArray","name","method","cache","complete","response","result","responseJSON","hasOwnProperty","map","children","text","trim","unshift","apply","success"],"mappings":"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","file":"wc-square.min.js"}
i18n/languages/woocommerce-square.pot CHANGED
@@ -2,10 +2,10 @@
2
  # This file is distributed under the GNU General Public License v3.0.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: WooCommerce Square 2.1.6\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://github.com/woocommerce/woocommerce-square/issues\n"
8
- "POT-Creation-Date: 2020-07-15 06:10:22+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
@@ -42,42 +42,42 @@ msgstr ""
42
  msgid "Could not delete records."
43
  msgstr ""
44
 
45
- #: includes/AJAX.php:206
46
  msgid "Could not delete record."
47
  msgstr ""
48
 
49
- #: includes/AJAX.php:217
50
  msgid "Could not resolve record."
51
  msgstr ""
52
 
53
- #: includes/AJAX.php:230
54
  msgid "Could not unsync product."
55
  msgstr ""
56
 
57
- #: includes/AJAX.php:242
58
  #. translators: Placeholder: %s - error message
59
  msgid "An error occurred. %s"
60
  msgstr ""
61
 
62
- #: includes/AJAX.php:287
63
  #. translators: Placeholder: %s - sync job ID
64
  msgid "No sync job in progress found %s"
65
  msgstr ""
66
 
67
- #: includes/AJAX.php:324
68
  msgid "Stock must be fetched from Square before editing stock quantity"
69
  msgstr ""
70
 
71
- #: includes/Admin/Privacy.php:46 includes/Admin/Settings_Page.php:55
72
- #: includes/Gateway.php:58 includes/Settings.php:190 includes/Settings.php:686
73
  msgid "Square"
74
  msgstr ""
75
 
76
- #: includes/Admin/Privacy.php:48
77
  msgid "WooCommerce Square Customer Data"
78
  msgstr ""
79
 
80
- #: includes/Admin/Privacy.php:62
81
  #. translators: Placeholder: %1$s - <a> tag, %2$s - </a> tag
82
  msgid ""
83
  "By using this extension, you may be storing personal data or sharing data "
@@ -101,27 +101,30 @@ msgstr ""
101
  msgid "Import Products From Square"
102
  msgstr ""
103
 
104
- #: includes/Admin/Settings_Page.php:175 includes/Admin/Sync_Page.php:336
105
  msgid "Close modal window"
106
  msgstr ""
107
 
108
  #: includes/Admin/Settings_Page.php:180
109
  #. translators: Placeholders: %1$s - <strong>, %2%s - </strong>
110
  msgid ""
111
- "You are about to import all products from Square. This will create a new "
112
- "product in WooCommerce for every product retrieved from Square. If you have "
113
- "products in the trash from the previous imports, these will be ignored in "
114
- "the import. %1$sOnly use this action to perform a one-time import!%2$s"
 
 
 
115
  msgstr ""
116
 
117
- #: includes/Admin/Settings_Page.php:184 includes/Admin/Sync_Page.php:368
118
  #: vendor/prospress/action-scheduler/classes/ActionScheduler_ListTable.php:133
119
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/admin/views/html-order-partial-capture.php:66
120
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway-my-payment-methods.php:227
121
  msgid "Cancel"
122
  msgstr ""
123
 
124
- #: includes/Admin/Settings_Page.php:185 includes/Settings.php:211
125
  msgid "Import Products"
126
  msgstr ""
127
 
@@ -129,7 +132,7 @@ msgstr ""
129
  msgid "Sync records"
130
  msgstr ""
131
 
132
- #: includes/Admin/Sync_Page.php:79
133
  #. translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s,
134
  #. $4%s - closing </strong> HTML tag
135
  msgid ""
@@ -138,7 +141,7 @@ msgid ""
138
  "description, category, inventory%4$s."
139
  msgstr ""
140
 
141
- #: includes/Admin/Sync_Page.php:88
142
  #. translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing
143
  #. </strong> HTML tag
144
  msgid ""
@@ -146,7 +149,7 @@ msgid ""
146
  "set in WooCommerce."
147
  msgstr ""
148
 
149
- #: includes/Admin/Sync_Page.php:100
150
  #. translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s,
151
  #. %4$s - closing </strong> HTML tag
152
  msgid ""
@@ -155,7 +158,7 @@ msgid ""
155
  "price, inventory, category, image%4$s."
156
  msgstr ""
157
 
158
- #: includes/Admin/Sync_Page.php:112
159
  #. translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing
160
  #. </strong> HTML tag
161
  msgid ""
@@ -163,46 +166,46 @@ msgid ""
163
  "Square and WooCommerce."
164
  msgstr ""
165
 
166
- #: includes/Admin/Sync_Page.php:145
167
  msgid "Please connect to Square to enable product sync."
168
  msgstr ""
169
 
170
- #: includes/Admin/Sync_Page.php:147
171
  msgid "Please set the business location to enable product sync."
172
  msgstr ""
173
 
174
- #: includes/Admin/Sync_Page.php:149
175
  msgid "There are currently no products marked to be synced with Square."
176
  msgstr ""
177
 
178
- #: includes/Admin/Sync_Page.php:151
179
  msgid "A sync is currently in progress. Please try again later."
180
  msgstr ""
181
 
182
- #: includes/Admin/Sync_Page.php:156
183
  #. translators: Placeholder: %s - reason text
184
  msgid "Product sync between WooCommerce and Square is currently unavailable. %s"
185
  msgstr ""
186
 
187
- #: includes/Admin/Sync_Page.php:163
188
  msgid "Synced products"
189
  msgstr ""
190
 
191
- #: includes/Admin/Sync_Page.php:164
192
  msgid "Latest sync"
193
  msgstr ""
194
 
195
- #: includes/Admin/Sync_Page.php:165
196
  msgid "Next sync"
197
  msgstr ""
198
 
199
- #: includes/Admin/Sync_Page.php:166 includes/Admin/Sync_Page.php:272
200
- #: includes/Admin/Sync_Page.php:313
201
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway-my-payment-methods.php:426
202
  msgid "Actions"
203
  msgstr ""
204
 
205
- #: includes/Admin/Sync_Page.php:175 includes/Emails/Sync_Completed.php:204
206
  #. translators: Placeholder: %d number of products synced with Square
207
  #. translators: Placeholder: %d products count
208
  msgid "%d product"
@@ -210,66 +213,66 @@ msgid_plural "%d products"
210
  msgstr[0] ""
211
  msgstr[1] ""
212
 
213
- #: includes/Admin/Sync_Page.php:192
214
  msgid "Importing now&hellip;"
215
  msgstr ""
216
 
217
- #: includes/Admin/Sync_Page.php:194
218
  msgid "Syncing now&hellip;"
219
  msgstr ""
220
 
221
- #: includes/Admin/Sync_Page.php:209
222
  msgid "Not synced yet."
223
  msgstr ""
224
 
225
- #: includes/Admin/Sync_Page.php:241
226
  msgid "Sync now"
227
  msgstr ""
228
 
229
- #: includes/Admin/Sync_Page.php:270 includes/Admin/Sync_Page.php:311
230
  #: vendor/prospress/action-scheduler/classes/ActionScheduler_ListTable.php:97
231
  msgid "Status"
232
  msgstr ""
233
 
234
- #: includes/Admin/Sync_Page.php:271 includes/Admin/Sync_Page.php:312
235
  msgid "Message"
236
  msgstr ""
237
 
238
- #: includes/Admin/Sync_Page.php:302
239
  msgid "No records found."
240
  msgstr ""
241
 
242
- #: includes/Admin/Sync_Page.php:334
243
  msgid "Sync products with Square"
244
  msgstr ""
245
 
246
- #: includes/Admin/Sync_Page.php:344
247
  msgid ""
248
  "If a match is found in Square, product data in WooCommerce will be "
249
  "overwritten with Square data."
250
  msgstr ""
251
 
252
- #: includes/Admin/Sync_Page.php:346
253
  msgid ""
254
  "If a match is found in WooCommerce, product data in Square will be "
255
  "overwritten with WooCommerce data."
256
  msgstr ""
257
 
258
- #: includes/Admin/Sync_Page.php:350
259
  msgid ""
260
  "If a match is not found in Square, the product will be hidden from the "
261
  "catalog in WooCommerce."
262
  msgstr ""
263
 
264
- #: includes/Admin/Sync_Page.php:352
265
  msgid "If a match is not found in Square, the product will be skipped in the sync."
266
  msgstr ""
267
 
268
- #: includes/Admin/Sync_Page.php:355
269
  msgid "If a match is not found, a new product will be created in Square."
270
  msgstr ""
271
 
272
- #: includes/Admin/Sync_Page.php:361
273
  #. translators: Placeholders: %1$s - the system of record name (e.g. Square or
274
  #. WooCommerce), %3%s - unordered HTML list of additional information item(s)
275
  msgid ""
@@ -277,51 +280,55 @@ msgid ""
277
  "For all products synced with Square: %2$s"
278
  msgstr ""
279
 
280
- #: includes/Admin/Sync_Page.php:369
281
  msgid "Start sync"
282
  msgstr ""
283
 
284
- #: includes/Admin.php:115 includes/Handlers/Products.php:176
285
  msgid "Synced with Square"
286
  msgstr ""
287
 
288
- #: includes/Admin.php:116
289
  msgid "Managed by Square"
290
  msgstr ""
291
 
292
- #: includes/Admin.php:117
293
  msgid "Fetch stock from Square"
294
  msgstr ""
295
 
296
- #: includes/Admin.php:149 includes/Sync/Records/Record.php:206
297
  msgid "Resolved"
298
  msgstr ""
299
 
300
- #: includes/Admin.php:150
301
  msgid "No records found"
302
  msgstr ""
303
 
304
- #: includes/Admin.php:151
305
  msgid "Skipped"
306
  msgstr ""
307
 
308
- #: includes/Admin.php:152
 
 
 
 
309
  msgid "Imported"
310
  msgstr ""
311
 
312
- #: includes/Admin.php:154
313
  msgid "Enable to fetch inventory changes from Square"
314
  msgstr ""
315
 
316
- #: includes/Admin.php:155
317
  msgid "Enable to push inventory changes to Square"
318
  msgstr ""
319
 
320
- #: includes/Admin.php:158 includes/Settings.php:200
321
  msgid "Inventory is fetched from Square periodically and updated in WooCommerce"
322
  msgstr ""
323
 
324
- #: includes/Admin.php:161
325
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag
326
  msgid ""
327
  "Inventory is %1$salways fetched from Square%2$s periodically to account for "
@@ -344,14 +351,14 @@ msgid ""
344
  "re-connect your site%2$s."
345
  msgstr ""
346
 
347
- #: includes/Emails/Access_Token_Email.php:106
348
  #: includes/Emails/Sync_Completed.php:100
349
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
350
  #. </a> HTML link tag
351
  msgid "%1$sInspect status logs%2$s"
352
  msgstr ""
353
 
354
- #: includes/Emails/Access_Token_Email.php:111
355
  msgid ""
356
  "In order to continue accepting payments, please disconnect and re-connect "
357
  "your site at "
@@ -364,12 +371,11 @@ msgid ""
364
  "subject: %s"
365
  msgstr ""
366
 
367
- #: includes/Emails/Base_Email.php:111
368
  msgid "Recipient(s)"
369
  msgstr ""
370
 
371
- #: includes/Emails/Base_Email.php:114
372
- #. translators: Placeholder: %s default email address
373
  msgid ""
374
  "Enter recipients (comma separated) for this email. Defaults to admin email: "
375
  "%s"
@@ -389,114 +395,118 @@ msgstr ""
389
  msgid "Square sync failed"
390
  msgstr ""
391
 
392
- #: includes/Emails/Sync_Completed.php:106
393
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
394
  #. </a> HTML link tag
395
  msgid "%1$sEnable logging%2$s"
396
  msgstr ""
397
 
398
- #: includes/Emails/Sync_Completed.php:113
399
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
400
  #. </a> HTML link tag, %3$s - additional action
401
  msgid "The sync job has failed. %1$sClick for more details%2$s, or %3$s."
402
  msgstr ""
403
 
404
- #: includes/Emails/Sync_Completed.php:121
405
  msgid "Inspect status logs"
406
  msgstr ""
407
 
408
- #: includes/Emails/Sync_Completed.php:123 includes/Settings.php:236
409
  msgid "Enable Logging"
410
  msgstr ""
411
 
412
- #: includes/Emails/Sync_Completed.php:128
413
  #. translators: Placeholders: %s - additional action
414
  msgid "The sync job has failed. Check sync records, or %s."
415
  msgstr ""
416
 
417
- #: includes/Gateway/API/Requests/Orders.php:82
418
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/apple-pay/class-sv-wc-payment-gateway-apple-pay.php:555
419
  msgid "Discount"
420
  msgstr ""
421
 
422
- #: includes/Gateway/API/Requests/Orders.php:281
423
- #: includes/Gateway/API/Requests/Orders.php:319
424
  msgid "Adjustment"
425
  msgstr ""
426
 
427
- #: includes/Gateway/Payment_Form.php:150
428
  msgid "Postal code"
429
  msgstr ""
430
 
431
- #: includes/Gateway/Payment_Form.php:203 includes/Gateway.php:195
432
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php:216
433
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway.php:2758
434
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-subscriptions.php:376
435
  msgid "An error occurred, please try again or try an alternate form of payment."
436
  msgstr ""
437
 
438
- #: includes/Gateway.php:59
439
  msgid "Allow customers to use Square to securely pay with their credit cards"
440
  msgstr ""
441
 
442
- #: includes/Gateway.php:385
443
- msgid "Refunds must be made within 120 days of the original payment date."
444
  msgstr ""
445
 
446
- #: includes/Gateway.php:410
447
  msgid ""
448
  "Could not find original transaction tender. Please refund this transaction "
449
  "from your Square dashboard."
450
  msgstr ""
451
 
452
- #: includes/Gateway.php:525
453
  msgid "Customer Profiles"
454
  msgstr ""
455
 
456
- #: includes/Handlers/Background_Job.php:336
 
 
 
 
457
  msgid "Clear Square Sync"
458
  msgstr ""
459
 
460
- #: includes/Handlers/Background_Job.php:337
461
  msgid "Clear"
462
  msgstr ""
463
 
464
- #: includes/Handlers/Background_Job.php:338
465
  msgid "This tool will clear any ongoing Square product syncs."
466
  msgstr ""
467
 
468
- #: includes/Handlers/Background_Job.php:357
469
  msgid "Success! You can now sync your products."
470
  msgstr ""
471
 
472
- #: includes/Handlers/Connection.php:103 includes/Handlers/Connection.php:168
473
  msgid "Sorry, you do not have permission to manage the Square connection."
474
  msgstr ""
475
 
476
- #: includes/Handlers/Connection.php:113
477
  msgid "Square Error: We could not connect to Square. No access token was given.!"
478
  msgstr ""
479
 
480
- #: includes/Handlers/Connection.php:176
481
  msgid "Disconnected successfully"
482
  msgstr ""
483
 
484
- #: includes/Handlers/Connection.php:385
485
  msgid "Connect with Square"
486
  msgstr ""
487
 
488
- #: includes/Handlers/Connection.php:405
489
  msgid "Disconnect from Square"
490
  msgstr ""
491
 
492
- #: includes/Handlers/Product.php:179
493
  #. translators: Placeholder: %s category ID
494
  msgid ""
495
  "Square category with id (%s) was not imported to your Store. Please run "
496
  "Import Products from Square settings."
497
  msgstr ""
498
 
499
- #: includes/Handlers/Product.php:294
500
  msgid "Product not synced with Square"
501
  msgstr ""
502
 
@@ -519,39 +529,39 @@ msgstr ""
519
  msgid "%s has multiple variation attributes and cannot be synced with Square."
520
  msgstr ""
521
 
522
- #: includes/Handlers/Products.php:249
523
  msgid "Update product data with Square data"
524
  msgstr ""
525
 
526
- #: includes/Handlers/Products.php:249
527
  msgid "Send product data to Square"
528
  msgstr ""
529
 
530
- #: includes/Handlers/Products.php:253
531
  msgid "Sync with Square"
532
  msgstr ""
533
 
534
- #: includes/Handlers/Products.php:288
535
  msgid "Sync with Square?"
536
  msgstr ""
537
 
538
- #: includes/Handlers/Products.php:292
539
  msgid "No change"
540
  msgstr ""
541
 
542
- #: includes/Handlers/Products.php:294
543
  msgid "No"
544
  msgstr ""
545
 
546
- #: includes/Handlers/Products.php:295
547
  msgid "Yes"
548
  msgstr ""
549
 
550
- #: includes/Handlers/Products.php:301
551
  msgid "This product"
552
  msgstr ""
553
 
554
- #: includes/Handlers/Products.php:655
555
  #. translators: Placeholder: %1$s - date (localized), %2$s - time (localized),
556
  #. %3$s - opening <a> HTML link tag, %4$s closing </a> HTML link tag
557
  msgid ""
@@ -560,13 +570,42 @@ msgid ""
560
  "records%4$s."
561
  msgstr ""
562
 
563
- #: includes/Handlers/Sync.php:318
564
  #. translators: Placeholder: %d number of products processed
565
  msgid "Updated data for %d product."
566
  msgid_plural "Updated data for %d products."
567
  msgstr[0] ""
568
  msgstr[1] ""
569
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
  #: includes/Plugin.php:275
571
  msgid ""
572
  "Heads up! There may be a problem with your connection to Square. In order "
@@ -574,24 +613,24 @@ msgid ""
574
  "site%2$s."
575
  msgstr ""
576
 
577
- #: includes/Plugin.php:287
578
  msgid "You are connected to Square!"
579
  msgstr ""
580
 
581
- #: includes/Plugin.php:294
582
  msgid "To get started, set your business location."
583
  msgstr ""
584
 
585
- #: includes/Plugin.php:300
586
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
587
  msgid "Visit the %1$splugin settings%2$s to set your business location."
588
  msgstr ""
589
 
590
- #: includes/Plugin.php:309
591
  msgid "You are ready to sync products!"
592
  msgstr ""
593
 
594
- #: includes/Plugin.php:315
595
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - product count, %3$s -
596
  #. </strong> tag, %4$s - <a> tag, %5$s - </a> tag
597
  msgid ""
@@ -599,7 +638,7 @@ msgid ""
599
  "now &raquo;%5$s"
600
  msgstr ""
601
 
602
- #: includes/Plugin.php:324
603
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
604
  #. <a> tag, %4$s - </a> tag
605
  msgid ""
@@ -607,28 +646,28 @@ msgid ""
607
  "products to sync data &raquo;%4$s"
608
  msgstr ""
609
 
610
- #: includes/Plugin.php:337
611
  msgid ""
612
  "Heads up! Square is configured to sync product inventory, but WooCommerce "
613
  "stock management is disabled. Please %1$senable stock management%2$s to "
614
  "ensure product inventory counts are kept in sync."
615
  msgstr ""
616
 
617
- #: includes/Plugin.php:350
618
  msgid "To get started, connect with Square."
619
  msgstr ""
620
 
621
- #: includes/Plugin.php:356
622
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
623
  msgid "To get started, %1$sconnect with Square &raquo;%2$s"
624
  msgstr ""
625
 
626
- #: includes/Plugin.php:363
627
  #. translators: Placeholders: %1$s - plugin name
628
  msgid "Thanks for installing %1$s!"
629
  msgstr ""
630
 
631
- #: includes/Plugin.php:387
632
  #. translators: Placeholders: %1$s - plugin name, %2$ - plugin version number,
633
  #. %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag, %5$s -
634
  #. opening <a> HTML link tag, %6$s - closing </a> HTML link tag
@@ -639,7 +678,7 @@ msgid ""
639
  "%5$supdated documentation%6$s."
640
  msgstr ""
641
 
642
- #: includes/Plugin.php:421
643
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
644
  #. 2-character country code, %4$s - comma separated list of 2-character country
645
  #. codes
@@ -648,7 +687,7 @@ msgid ""
648
  "accept transactions from merchants outside of %4$s."
649
  msgstr ""
650
 
651
- #: includes/Plugin.php:443
652
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
653
  #. <a> tag, %4$s - </a> tag
654
  msgid ""
@@ -657,7 +696,7 @@ msgid ""
657
  "successfully with Square. %3$sRead more here%4$s on how to resolve this."
658
  msgstr ""
659
 
660
- #: includes/Plugin.php:481
661
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
662
  #. <a> tag, %4$s - </a> tag
663
  msgid ""
@@ -665,14 +704,14 @@ msgid ""
665
  "Square is inactive. Please disconnect and reconnect to resolve."
666
  msgstr ""
667
 
668
- #: includes/Plugin.php:512
669
  msgid ""
670
  "%1$sWooCommerce Square:%2$s Product prices are entered inclusive of tax, "
671
  "but Square does not support syncing tax-inclusive prices. Please make sure "
672
  "your Square tax rates match your WooCommerce tax rates."
673
  msgstr ""
674
 
675
- #: includes/Plugin.php:538
676
  msgid ""
677
  "Heads up! Your store currency is %1$s but your configured Square business "
678
  "location currency is %2$s, so payments cannot be processed. Please "
@@ -684,57 +723,57 @@ msgstr ""
684
  msgid "WooCommerce Square"
685
  msgstr ""
686
 
687
- #: includes/Settings.php:117
688
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
689
  msgid ""
690
  "Sync your products and inventory and also accept credit and debit card "
691
  "payments at checkout. %1$sClick here%2$s to configure payments."
692
  msgstr ""
693
 
694
- #: includes/Settings.php:123
695
  msgid ""
696
  "Connect with Square to start syncing your products and inventory and also "
697
  "accept credit and debit card payments at checkout."
698
  msgstr ""
699
 
700
- #: includes/Settings.php:134
701
  msgid "Enable Sandbox Mode"
702
  msgstr ""
703
 
704
- #: includes/Settings.php:135
705
  msgid "Enable to set the plugin in sandbox mode."
706
  msgstr ""
707
 
708
- #: includes/Settings.php:137
709
  msgid ""
710
  "After enabling you’ll see a new Sandbox settings section with two fields; "
711
  "Sandbox Application ID & Sandbox Access Token."
712
  msgstr ""
713
 
714
- #: includes/Settings.php:143
715
  msgid "Sandbox settings"
716
  msgstr ""
717
 
718
- #: includes/Settings.php:146
719
- #. translators: Placeholders: %1$s - URL
720
  msgid "Sandbox details can be created at: %s"
721
  msgstr ""
722
 
723
- #: includes/Settings.php:152
724
  msgid "Sandbox Application ID"
725
  msgstr ""
726
 
727
- #: includes/Settings.php:153
728
  msgid ""
729
  "Application ID for the Sandbox Application, see the details in the My "
730
  "Applications section."
731
  msgstr ""
732
 
733
- #: includes/Settings.php:157
734
  msgid "Sandbox Access Token"
735
  msgstr ""
736
 
737
- #: includes/Settings.php:158
738
  msgid ""
739
  "Access Token for the Sandbox Test Account, see the details in the Sandbox "
740
  "Test Account section. Make sure you use the correct Sandbox Access Token "
@@ -742,11 +781,11 @@ msgid ""
742
  "Application is assigned a different Access Token."
743
  msgstr ""
744
 
745
- #: includes/Settings.php:166
746
  msgid "Business location"
747
  msgstr ""
748
 
749
- #: includes/Settings.php:171
750
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
751
  #. <a> tag, %4$s - </a> tag
752
  msgid ""
@@ -755,11 +794,11 @@ msgid ""
755
  "linked."
756
  msgstr ""
757
 
758
- #: includes/Settings.php:179
759
  msgid "Product system of record"
760
  msgstr ""
761
 
762
- #: includes/Settings.php:184
763
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
764
  #. <a> tag, %4$s - </a> tag
765
  msgid ""
@@ -768,7 +807,7 @@ msgid ""
768
  "here%4$s to read more about choosing a system of record."
769
  msgstr ""
770
 
771
- #: includes/Settings.php:189
772
  msgid "Do not sync product data"
773
  msgstr ""
774
 
@@ -776,135 +815,130 @@ msgstr ""
776
  msgid "WooCommerce"
777
  msgstr ""
778
 
779
- #: includes/Settings.php:197
780
  msgid "Sync inventory"
781
  msgstr ""
782
 
783
- #: includes/Settings.php:198
784
  msgid "Enable to sync product inventory with Square"
785
  msgstr ""
786
 
787
- #: includes/Settings.php:204
788
  msgid "Handle missing products"
789
  msgstr ""
790
 
791
- #: includes/Settings.php:205
792
  msgid "Hide synced products when not found in Square"
793
  msgstr ""
794
 
795
- #: includes/Settings.php:207
796
  msgid ""
797
  "Products not found in Square will be hidden in the WooCommerce product "
798
  "catalog."
799
  msgstr ""
800
 
801
- #: includes/Settings.php:213
802
  msgid ""
803
  "Run an import to create new products in this WooCommerce store for each new "
804
  "product created in Square that has a unique SKU not existing in here. Needs "
805
  "to be run each time new items are created in Square."
806
  msgstr ""
807
 
808
- #: includes/Settings.php:223
809
  msgid "Connection"
810
  msgstr ""
811
 
812
- #: includes/Settings.php:240
813
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
814
  msgid "Log debug messages to the %1$sWooCommerce status log%2$s"
815
  msgstr ""
816
 
817
- #: includes/Settings.php:266
818
  msgid "Please choose a location"
819
  msgstr ""
820
 
821
- #: includes/Settings.php:297
822
  msgid "Import all products from Square"
823
  msgstr ""
824
 
825
- #: includes/Sync/Interval_Polling.php:96
826
  msgid "Updated data for %d category."
827
  msgid_plural "Updated data for %d categories."
828
  msgstr[0] ""
829
  msgstr[1] ""
830
 
831
- #: includes/Sync/Manual_Synchronization.php:1099
832
  #. translators: Placeholder: %s - product ID
833
  msgid "Product %s could not be updated in Square."
834
  msgstr ""
835
 
836
- #: includes/Sync/Product_Import.php:264
837
- msgid "You do not have permission to create products"
838
- msgstr ""
839
-
840
- #: includes/Sync/Product_Import.php:269
841
- msgid "Invalid data"
842
- msgstr ""
843
-
844
- #: includes/Sync/Product_Import.php:287
845
- msgid "Missing parameter %s"
846
  msgstr ""
847
 
848
- #: includes/Sync/Product_Import.php:302
849
- msgid "Invalid product type - the product type must be any of these: %s"
850
  msgstr ""
851
 
852
- #: includes/Sync/Product_Import.php:365
853
- #. translators: Placeholders: %1$s - Square item name, %2$s - failure reason
854
- msgid "Could not import \"%1$s\" from Square. %2$s"
855
  msgstr ""
856
 
857
- #: includes/Sync/Product_Import.php:375
858
- #. translators: Placeholders: %s - failure reason
859
- msgid "Could not import item from Square. %s"
860
  msgstr ""
861
 
862
- #: includes/Sync/Product_Import.php:453
863
- #. translators: Placeholders: %1$s - Square item name, %2$s - Square item
864
- #. variation name, %3$s - failure reason
865
- msgid "Could not import \"%1$s - %2$s\" from Square. %3$s"
866
  msgstr ""
867
 
868
- #: includes/Sync/Product_Import.php:504
869
- msgid "Items with variable pricing cannot be imported."
 
870
  msgstr ""
871
 
872
- #: includes/Sync/Product_Import.php:602 includes/Sync/Product_Import.php:866
873
- msgid "The SKU already exists on another product"
 
874
  msgstr ""
875
 
876
- #: includes/Sync/Product_Import.php:790
877
- msgid "Variation #%1$s of %2$s"
 
878
  msgstr ""
879
 
880
- #: includes/Sync/Records/Record.php:203
881
  msgid "Info"
882
  msgstr ""
883
 
884
- #: includes/Sync/Records/Record.php:204
885
  msgid "Notice"
886
  msgstr ""
887
 
888
- #: includes/Sync/Records/Record.php:205
889
  msgid "Alert"
890
  msgstr ""
891
 
892
- #: includes/Sync/Records/Record.php:371
893
  #. translators: Placeholder: %s - product name
894
  msgid "%s not found in Square."
895
  msgstr ""
896
 
897
- #: includes/Sync/Records/Record.php:542
898
  #: vendor/prospress/action-scheduler/classes/ActionScheduler_ListTable.php:92
899
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway-my-payment-methods.php:819
900
  msgid "Delete"
901
  msgstr ""
902
 
903
- #: includes/Sync/Records/Record.php:550
904
  msgid "Ignore"
905
  msgstr ""
906
 
907
- #: includes/Sync/Records/Record.php:559
908
  msgid "Unlink"
909
  msgstr ""
910
 
@@ -912,32 +946,32 @@ msgstr ""
912
  msgid "Encryption is not supported on this site."
913
  msgstr ""
914
 
915
- #: includes/Utilities/Encryption_Utility.php:74
916
  msgid "%1$s encryption is not available on this site. %2$s will be used instead."
917
  msgstr ""
918
 
919
- #: includes/Utilities/Encryption_Utility.php:99
920
  msgid "No encryption method available"
921
  msgstr ""
922
 
923
- #: includes/Utilities/Encryption_Utility.php:103
924
  msgid "Data must be a non-empty string or array"
925
  msgstr ""
926
 
927
- #: includes/Utilities/Encryption_Utility.php:107
928
- #: includes/Utilities/Encryption_Utility.php:150
929
  msgid "Encryption key must be a string"
930
  msgstr ""
931
 
932
- #: includes/Utilities/Encryption_Utility.php:119
933
  msgid "Could not generate encryption vector."
934
  msgstr ""
935
 
936
- #: includes/Utilities/Encryption_Utility.php:142
937
  msgid "No decryption method available"
938
  msgstr ""
939
 
940
- #: includes/Utilities/Encryption_Utility.php:146
941
  msgid "Data must be a non-empty string"
942
  msgstr ""
943
 
@@ -1329,35 +1363,14 @@ msgstr ""
1329
  msgid "Action Group"
1330
  msgstr ""
1331
 
1332
- #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:374
1333
- msgid "Awesome"
1334
- msgstr ""
1335
-
1336
- #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:375
1337
- msgid "Fantastic"
1338
- msgstr ""
1339
-
1340
  #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:376
1341
  msgid "Cowabunga"
1342
  msgstr ""
1343
 
1344
- #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:377
1345
- msgid "Congratulations"
1346
- msgstr ""
1347
-
1348
  #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:378
1349
  msgid "Hot dog"
1350
  msgstr ""
1351
 
1352
- #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:385
1353
- #. translators: Placeholders: %1$s - plugin name, %2$s - <a> tag, %3$s - </a>
1354
- #. tag, %4$s - <a> tag, %5$s - </a> tag
1355
- msgid ""
1356
- "Are you having a great experience with %1$s so far? Please consider "
1357
- "%2$sleaving a review%3$s! If things aren't going quite as expected, we're "
1358
- "happy to help -- please %4$sreach out to our support team%5$s."
1359
- msgstr ""
1360
-
1361
  #: vendor/skyverge/wc-plugin-framework/woocommerce/admin/abstract-sv-wc-plugin-admin-setup-wizard.php:183
1362
  msgid ""
1363
  "Thanks for installing %1$s! To get started, take a minute to %2$sread the "
@@ -3037,12 +3050,12 @@ msgstr ""
3037
  msgid "https://www.woocommerce.com/"
3038
  msgstr ""
3039
 
3040
- #: includes/Admin/Sync_Page.php:264
3041
  msgctxt "Delete all records"
3042
  msgid "Clear history"
3043
  msgstr ""
3044
 
3045
- #: includes/Admin/Sync_Page.php:269 includes/Admin/Sync_Page.php:310
3046
  msgctxt "Date - Time"
3047
  msgid "Time"
3048
  msgstr ""
2
  # This file is distributed under the GNU General Public License v3.0.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: WooCommerce Square 2.2.0\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://github.com/woocommerce/woocommerce-square/issues\n"
8
+ "POT-Creation-Date: 2020-08-31 01:32:52+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
42
  msgid "Could not delete records."
43
  msgstr ""
44
 
45
+ #: includes/AJAX.php:205
46
  msgid "Could not delete record."
47
  msgstr ""
48
 
49
+ #: includes/AJAX.php:215
50
  msgid "Could not resolve record."
51
  msgstr ""
52
 
53
+ #: includes/AJAX.php:227
54
  msgid "Could not unsync product."
55
  msgstr ""
56
 
57
+ #: includes/AJAX.php:239
58
  #. translators: Placeholder: %s - error message
59
  msgid "An error occurred. %s"
60
  msgstr ""
61
 
62
+ #: includes/AJAX.php:284
63
  #. translators: Placeholder: %s - sync job ID
64
  msgid "No sync job in progress found %s"
65
  msgstr ""
66
 
67
+ #: includes/AJAX.php:322
68
  msgid "Stock must be fetched from Square before editing stock quantity"
69
  msgstr ""
70
 
71
+ #: includes/Admin/Privacy.php:45 includes/Admin/Settings_Page.php:55
72
+ #: includes/Gateway.php:70 includes/Settings.php:223 includes/Settings.php:757
73
  msgid "Square"
74
  msgstr ""
75
 
76
+ #: includes/Admin/Privacy.php:47
77
  msgid "WooCommerce Square Customer Data"
78
  msgstr ""
79
 
80
+ #: includes/Admin/Privacy.php:61
81
  #. translators: Placeholder: %1$s - <a> tag, %2$s - </a> tag
82
  msgid ""
83
  "By using this extension, you may be storing personal data or sharing data "
101
  msgid "Import Products From Square"
102
  msgstr ""
103
 
104
+ #: includes/Admin/Settings_Page.php:175 includes/Admin/Sync_Page.php:359
105
  msgid "Close modal window"
106
  msgstr ""
107
 
108
  #: includes/Admin/Settings_Page.php:180
109
  #. translators: Placeholders: %1$s - <strong>, %2%s - </strong>
110
  msgid ""
111
+ "You are about to import all new products, variations and categories from "
112
+ "Square. This will create a new product in WooCommerce for every product "
113
+ "retrieved from Square. If you have products in the trash from the previous "
114
+ "imports, these will be ignored in the import. During this process, simple "
115
+ "products that have new variations found in Sqaure will be converted to a "
116
+ "variable product in WooCommerce. %1$sOnly use this action to perform a "
117
+ "one-time import!%2$s"
118
  msgstr ""
119
 
120
+ #: includes/Admin/Settings_Page.php:184 includes/Admin/Sync_Page.php:393
121
  #: vendor/prospress/action-scheduler/classes/ActionScheduler_ListTable.php:133
122
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/admin/views/html-order-partial-capture.php:66
123
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway-my-payment-methods.php:227
124
  msgid "Cancel"
125
  msgstr ""
126
 
127
+ #: includes/Admin/Settings_Page.php:185 includes/Settings.php:244
128
  msgid "Import Products"
129
  msgstr ""
130
 
132
  msgid "Sync records"
133
  msgstr ""
134
 
135
+ #: includes/Admin/Sync_Page.php:80
136
  #. translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s,
137
  #. $4%s - closing </strong> HTML tag
138
  msgid ""
141
  "description, category, inventory%4$s."
142
  msgstr ""
143
 
144
+ #: includes/Admin/Sync_Page.php:94
145
  #. translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing
146
  #. </strong> HTML tag
147
  msgid ""
149
  "set in WooCommerce."
150
  msgstr ""
151
 
152
+ #: includes/Admin/Sync_Page.php:109
153
  #. translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s,
154
  #. %4$s - closing </strong> HTML tag
155
  msgid ""
158
  "price, inventory, category, image%4$s."
159
  msgstr ""
160
 
161
+ #: includes/Admin/Sync_Page.php:126
162
  #. translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing
163
  #. </strong> HTML tag
164
  msgid ""
166
  "Square and WooCommerce."
167
  msgstr ""
168
 
169
+ #: includes/Admin/Sync_Page.php:161
170
  msgid "Please connect to Square to enable product sync."
171
  msgstr ""
172
 
173
+ #: includes/Admin/Sync_Page.php:163
174
  msgid "Please set the business location to enable product sync."
175
  msgstr ""
176
 
177
+ #: includes/Admin/Sync_Page.php:165
178
  msgid "There are currently no products marked to be synced with Square."
179
  msgstr ""
180
 
181
+ #: includes/Admin/Sync_Page.php:167
182
  msgid "A sync is currently in progress. Please try again later."
183
  msgstr ""
184
 
185
+ #: includes/Admin/Sync_Page.php:172
186
  #. translators: Placeholder: %s - reason text
187
  msgid "Product sync between WooCommerce and Square is currently unavailable. %s"
188
  msgstr ""
189
 
190
+ #: includes/Admin/Sync_Page.php:179
191
  msgid "Synced products"
192
  msgstr ""
193
 
194
+ #: includes/Admin/Sync_Page.php:180
195
  msgid "Latest sync"
196
  msgstr ""
197
 
198
+ #: includes/Admin/Sync_Page.php:181
199
  msgid "Next sync"
200
  msgstr ""
201
 
202
+ #: includes/Admin/Sync_Page.php:182 includes/Admin/Sync_Page.php:295
203
+ #: includes/Admin/Sync_Page.php:336
204
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway-my-payment-methods.php:426
205
  msgid "Actions"
206
  msgstr ""
207
 
208
+ #: includes/Admin/Sync_Page.php:192 includes/Emails/Sync_Completed.php:207
209
  #. translators: Placeholder: %d number of products synced with Square
210
  #. translators: Placeholder: %d products count
211
  msgid "%d product"
213
  msgstr[0] ""
214
  msgstr[1] ""
215
 
216
+ #: includes/Admin/Sync_Page.php:210
217
  msgid "Importing now&hellip;"
218
  msgstr ""
219
 
220
+ #: includes/Admin/Sync_Page.php:212
221
  msgid "Syncing now&hellip;"
222
  msgstr ""
223
 
224
+ #: includes/Admin/Sync_Page.php:227
225
  msgid "Not synced yet."
226
  msgstr ""
227
 
228
+ #: includes/Admin/Sync_Page.php:259
229
  msgid "Sync now"
230
  msgstr ""
231
 
232
+ #: includes/Admin/Sync_Page.php:293 includes/Admin/Sync_Page.php:334
233
  #: vendor/prospress/action-scheduler/classes/ActionScheduler_ListTable.php:97
234
  msgid "Status"
235
  msgstr ""
236
 
237
+ #: includes/Admin/Sync_Page.php:294 includes/Admin/Sync_Page.php:335
238
  msgid "Message"
239
  msgstr ""
240
 
241
+ #: includes/Admin/Sync_Page.php:325
242
  msgid "No records found."
243
  msgstr ""
244
 
245
+ #: includes/Admin/Sync_Page.php:357
246
  msgid "Sync products with Square"
247
  msgstr ""
248
 
249
+ #: includes/Admin/Sync_Page.php:367
250
  msgid ""
251
  "If a match is found in Square, product data in WooCommerce will be "
252
  "overwritten with Square data."
253
  msgstr ""
254
 
255
+ #: includes/Admin/Sync_Page.php:369
256
  msgid ""
257
  "If a match is found in WooCommerce, product data in Square will be "
258
  "overwritten with WooCommerce data."
259
  msgstr ""
260
 
261
+ #: includes/Admin/Sync_Page.php:373
262
  msgid ""
263
  "If a match is not found in Square, the product will be hidden from the "
264
  "catalog in WooCommerce."
265
  msgstr ""
266
 
267
+ #: includes/Admin/Sync_Page.php:375
268
  msgid "If a match is not found in Square, the product will be skipped in the sync."
269
  msgstr ""
270
 
271
+ #: includes/Admin/Sync_Page.php:378
272
  msgid "If a match is not found, a new product will be created in Square."
273
  msgstr ""
274
 
275
+ #: includes/Admin/Sync_Page.php:385
276
  #. translators: Placeholders: %1$s - the system of record name (e.g. Square or
277
  #. WooCommerce), %3%s - unordered HTML list of additional information item(s)
278
  msgid ""
280
  "For all products synced with Square: %2$s"
281
  msgstr ""
282
 
283
+ #: includes/Admin/Sync_Page.php:394
284
  msgid "Start sync"
285
  msgstr ""
286
 
287
+ #: includes/Admin.php:143 includes/Handlers/Products.php:177
288
  msgid "Synced with Square"
289
  msgstr ""
290
 
291
+ #: includes/Admin.php:144
292
  msgid "Managed by Square"
293
  msgstr ""
294
 
295
+ #: includes/Admin.php:145
296
  msgid "Fetch stock from Square"
297
  msgstr ""
298
 
299
+ #: includes/Admin.php:192 includes/Sync/Records/Record.php:209
300
  msgid "Resolved"
301
  msgstr ""
302
 
303
+ #: includes/Admin.php:193
304
  msgid "No records found"
305
  msgstr ""
306
 
307
+ #: includes/Admin.php:194
308
  msgid "Skipped"
309
  msgstr ""
310
 
311
+ #: includes/Admin.php:195
312
+ msgid "Updated"
313
+ msgstr ""
314
+
315
+ #: includes/Admin.php:196
316
  msgid "Imported"
317
  msgstr ""
318
 
319
+ #: includes/Admin.php:198
320
  msgid "Enable to fetch inventory changes from Square"
321
  msgstr ""
322
 
323
+ #: includes/Admin.php:199
324
  msgid "Enable to push inventory changes to Square"
325
  msgstr ""
326
 
327
+ #: includes/Admin.php:202 includes/Settings.php:233
328
  msgid "Inventory is fetched from Square periodically and updated in WooCommerce"
329
  msgstr ""
330
 
331
+ #: includes/Admin.php:205
332
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag
333
  msgid ""
334
  "Inventory is %1$salways fetched from Square%2$s periodically to account for "
351
  "re-connect your site%2$s."
352
  msgstr ""
353
 
354
+ #: includes/Emails/Access_Token_Email.php:107
355
  #: includes/Emails/Sync_Completed.php:100
356
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
357
  #. </a> HTML link tag
358
  msgid "%1$sInspect status logs%2$s"
359
  msgstr ""
360
 
361
+ #: includes/Emails/Access_Token_Email.php:113
362
  msgid ""
363
  "In order to continue accepting payments, please disconnect and re-connect "
364
  "your site at "
371
  "subject: %s"
372
  msgstr ""
373
 
374
+ #: includes/Emails/Base_Email.php:114
375
  msgid "Recipient(s)"
376
  msgstr ""
377
 
378
+ #: includes/Emails/Base_Email.php:117
 
379
  msgid ""
380
  "Enter recipients (comma separated) for this email. Defaults to admin email: "
381
  "%s"
395
  msgid "Square sync failed"
396
  msgstr ""
397
 
398
+ #: includes/Emails/Sync_Completed.php:107
399
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
400
  #. </a> HTML link tag
401
  msgid "%1$sEnable logging%2$s"
402
  msgstr ""
403
 
404
+ #: includes/Emails/Sync_Completed.php:115
405
  #. translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing
406
  #. </a> HTML link tag, %3$s - additional action
407
  msgid "The sync job has failed. %1$sClick for more details%2$s, or %3$s."
408
  msgstr ""
409
 
410
+ #: includes/Emails/Sync_Completed.php:124
411
  msgid "Inspect status logs"
412
  msgstr ""
413
 
414
+ #: includes/Emails/Sync_Completed.php:126 includes/Settings.php:269
415
  msgid "Enable Logging"
416
  msgstr ""
417
 
418
+ #: includes/Emails/Sync_Completed.php:131
419
  #. translators: Placeholders: %s - additional action
420
  msgid "The sync job has failed. Check sync records, or %s."
421
  msgstr ""
422
 
423
+ #: includes/Gateway/API/Requests/Orders.php:85
424
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/apple-pay/class-sv-wc-payment-gateway-apple-pay.php:555
425
  msgid "Discount"
426
  msgstr ""
427
 
428
+ #: includes/Gateway/API/Requests/Orders.php:294
429
+ #: includes/Gateway/API/Requests/Orders.php:340
430
  msgid "Adjustment"
431
  msgstr ""
432
 
433
+ #: includes/Gateway/Payment_Form.php:152
434
  msgid "Postal code"
435
  msgstr ""
436
 
437
+ #: includes/Gateway/Payment_Form.php:210 includes/Gateway.php:229
438
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php:216
439
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway.php:2758
440
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-subscriptions.php:376
441
  msgid "An error occurred, please try again or try an alternate form of payment."
442
  msgstr ""
443
 
444
+ #: includes/Gateway.php:71
445
  msgid "Allow customers to use Square to securely pay with their credit cards"
446
  msgstr ""
447
 
448
+ #: includes/Gateway.php:426
449
+ msgid "Refunds must be made within %s of the original payment date."
450
  msgstr ""
451
 
452
+ #: includes/Gateway.php:450
453
  msgid ""
454
  "Could not find original transaction tender. Please refund this transaction "
455
  "from your Square dashboard."
456
  msgstr ""
457
 
458
+ #: includes/Gateway.php:565
459
  msgid "Customer Profiles"
460
  msgstr ""
461
 
462
+ #: includes/Gateway.php:864
463
+ msgid " An error occurred, please try again or try an alternate form of payment."
464
+ msgstr ""
465
+
466
+ #: includes/Handlers/Background_Job.php:347
467
  msgid "Clear Square Sync"
468
  msgstr ""
469
 
470
+ #: includes/Handlers/Background_Job.php:348
471
  msgid "Clear"
472
  msgstr ""
473
 
474
+ #: includes/Handlers/Background_Job.php:349
475
  msgid "This tool will clear any ongoing Square product syncs."
476
  msgstr ""
477
 
478
+ #: includes/Handlers/Background_Job.php:368
479
  msgid "Success! You can now sync your products."
480
  msgstr ""
481
 
482
+ #: includes/Handlers/Connection.php:103 includes/Handlers/Connection.php:171
483
  msgid "Sorry, you do not have permission to manage the Square connection."
484
  msgstr ""
485
 
486
+ #: includes/Handlers/Connection.php:115
487
  msgid "Square Error: We could not connect to Square. No access token was given.!"
488
  msgstr ""
489
 
490
+ #: includes/Handlers/Connection.php:179
491
  msgid "Disconnected successfully"
492
  msgstr ""
493
 
494
+ #: includes/Handlers/Connection.php:386
495
  msgid "Connect with Square"
496
  msgstr ""
497
 
498
+ #: includes/Handlers/Connection.php:406
499
  msgid "Disconnect from Square"
500
  msgstr ""
501
 
502
+ #: includes/Handlers/Product.php:178
503
  #. translators: Placeholder: %s category ID
504
  msgid ""
505
  "Square category with id (%s) was not imported to your Store. Please run "
506
  "Import Products from Square settings."
507
  msgstr ""
508
 
509
+ #: includes/Handlers/Product.php:295
510
  msgid "Product not synced with Square"
511
  msgstr ""
512
 
529
  msgid "%s has multiple variation attributes and cannot be synced with Square."
530
  msgstr ""
531
 
532
+ #: includes/Handlers/Products.php:252
533
  msgid "Update product data with Square data"
534
  msgstr ""
535
 
536
+ #: includes/Handlers/Products.php:252
537
  msgid "Send product data to Square"
538
  msgstr ""
539
 
540
+ #: includes/Handlers/Products.php:257
541
  msgid "Sync with Square"
542
  msgstr ""
543
 
544
+ #: includes/Handlers/Products.php:293
545
  msgid "Sync with Square?"
546
  msgstr ""
547
 
548
+ #: includes/Handlers/Products.php:297
549
  msgid "No change"
550
  msgstr ""
551
 
552
+ #: includes/Handlers/Products.php:299
553
  msgid "No"
554
  msgstr ""
555
 
556
+ #: includes/Handlers/Products.php:300
557
  msgid "Yes"
558
  msgstr ""
559
 
560
+ #: includes/Handlers/Products.php:306
561
  msgid "This product"
562
  msgstr ""
563
 
564
+ #: includes/Handlers/Products.php:665
565
  #. translators: Placeholder: %1$s - date (localized), %2$s - time (localized),
566
  #. %3$s - opening <a> HTML link tag, %4$s closing </a> HTML link tag
567
  msgid ""
570
  "records%4$s."
571
  msgstr ""
572
 
573
+ #: includes/Handlers/Sync.php:327
574
  #. translators: Placeholder: %d number of products processed
575
  msgid "Updated data for %d product."
576
  msgid_plural "Updated data for %d products."
577
  msgstr[0] ""
578
  msgstr[1] ""
579
 
580
+ #: includes/Lifecycle.php:184
581
+ #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:374
582
+ msgid "Awesome"
583
+ msgstr ""
584
+
585
+ #: includes/Lifecycle.php:185
586
+ #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:377
587
+ msgid "Congratulations"
588
+ msgstr ""
589
+
590
+ #: includes/Lifecycle.php:186
591
+ msgid "Great"
592
+ msgstr ""
593
+
594
+ #: includes/Lifecycle.php:187
595
+ #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:375
596
+ msgid "Fantastic"
597
+ msgstr ""
598
+
599
+ #: includes/Lifecycle.php:194
600
+ #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:385
601
+ #. translators: Placeholders: %1$s - plugin name, %2$s - <a> tag, %3$s - </a>
602
+ #. tag, %4$s - <a> tag, %5$s - </a> tag
603
+ msgid ""
604
+ "Are you having a great experience with %1$s so far? Please consider "
605
+ "%2$sleaving a review%3$s! If things aren't going quite as expected, we're "
606
+ "happy to help -- please %4$sreach out to our support team%5$s."
607
+ msgstr ""
608
+
609
  #: includes/Plugin.php:275
610
  msgid ""
611
  "Heads up! There may be a problem with your connection to Square. In order "
613
  "site%2$s."
614
  msgstr ""
615
 
616
+ #: includes/Plugin.php:292
617
  msgid "You are connected to Square!"
618
  msgstr ""
619
 
620
+ #: includes/Plugin.php:299
621
  msgid "To get started, set your business location."
622
  msgstr ""
623
 
624
+ #: includes/Plugin.php:305
625
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
626
  msgid "Visit the %1$splugin settings%2$s to set your business location."
627
  msgstr ""
628
 
629
+ #: includes/Plugin.php:315
630
  msgid "You are ready to sync products!"
631
  msgstr ""
632
 
633
+ #: includes/Plugin.php:321
634
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - product count, %3$s -
635
  #. </strong> tag, %4$s - <a> tag, %5$s - </a> tag
636
  msgid ""
638
  "now &raquo;%5$s"
639
  msgstr ""
640
 
641
+ #: includes/Plugin.php:333
642
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
643
  #. <a> tag, %4$s - </a> tag
644
  msgid ""
646
  "products to sync data &raquo;%4$s"
647
  msgstr ""
648
 
649
+ #: includes/Plugin.php:348
650
  msgid ""
651
  "Heads up! Square is configured to sync product inventory, but WooCommerce "
652
  "stock management is disabled. Please %1$senable stock management%2$s to "
653
  "ensure product inventory counts are kept in sync."
654
  msgstr ""
655
 
656
+ #: includes/Plugin.php:365
657
  msgid "To get started, connect with Square."
658
  msgstr ""
659
 
660
+ #: includes/Plugin.php:371
661
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
662
  msgid "To get started, %1$sconnect with Square &raquo;%2$s"
663
  msgstr ""
664
 
665
+ #: includes/Plugin.php:379
666
  #. translators: Placeholders: %1$s - plugin name
667
  msgid "Thanks for installing %1$s!"
668
  msgstr ""
669
 
670
+ #: includes/Plugin.php:403
671
  #. translators: Placeholders: %1$s - plugin name, %2$ - plugin version number,
672
  #. %3$s - opening <a> HTML link tag, %4$s - closing </a> HTML link tag, %5$s -
673
  #. opening <a> HTML link tag, %6$s - closing </a> HTML link tag
678
  "%5$supdated documentation%6$s."
679
  msgstr ""
680
 
681
+ #: includes/Plugin.php:440
682
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
683
  #. 2-character country code, %4$s - comma separated list of 2-character country
684
  #. codes
687
  "accept transactions from merchants outside of %4$s."
688
  msgstr ""
689
 
690
+ #: includes/Plugin.php:467
691
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
692
  #. <a> tag, %4$s - </a> tag
693
  msgid ""
696
  "successfully with Square. %3$sRead more here%4$s on how to resolve this."
697
  msgstr ""
698
 
699
+ #: includes/Plugin.php:510
700
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
701
  #. <a> tag, %4$s - </a> tag
702
  msgid ""
704
  "Square is inactive. Please disconnect and reconnect to resolve."
705
  msgstr ""
706
 
707
+ #: includes/Plugin.php:542
708
  msgid ""
709
  "%1$sWooCommerce Square:%2$s Product prices are entered inclusive of tax, "
710
  "but Square does not support syncing tax-inclusive prices. Please make sure "
711
  "your Square tax rates match your WooCommerce tax rates."
712
  msgstr ""
713
 
714
+ #: includes/Plugin.php:573
715
  msgid ""
716
  "Heads up! Your store currency is %1$s but your configured Square business "
717
  "location currency is %2$s, so payments cannot be processed. Please "
723
  msgid "WooCommerce Square"
724
  msgstr ""
725
 
726
+ #: includes/Settings.php:142
727
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
728
  msgid ""
729
  "Sync your products and inventory and also accept credit and debit card "
730
  "payments at checkout. %1$sClick here%2$s to configure payments."
731
  msgstr ""
732
 
733
+ #: includes/Settings.php:149
734
  msgid ""
735
  "Connect with Square to start syncing your products and inventory and also "
736
  "accept credit and debit card payments at checkout."
737
  msgstr ""
738
 
739
+ #: includes/Settings.php:160
740
  msgid "Enable Sandbox Mode"
741
  msgstr ""
742
 
743
+ #: includes/Settings.php:161
744
  msgid "Enable to set the plugin in sandbox mode."
745
  msgstr ""
746
 
747
+ #: includes/Settings.php:163
748
  msgid ""
749
  "After enabling you’ll see a new Sandbox settings section with two fields; "
750
  "Sandbox Application ID & Sandbox Access Token."
751
  msgstr ""
752
 
753
+ #: includes/Settings.php:168
754
  msgid "Sandbox settings"
755
  msgstr ""
756
 
757
+ #: includes/Settings.php:172
758
+ #. translators: Placeholders: %1$s - URL.
759
  msgid "Sandbox details can be created at: %s"
760
  msgstr ""
761
 
762
+ #: includes/Settings.php:179
763
  msgid "Sandbox Application ID"
764
  msgstr ""
765
 
766
+ #: includes/Settings.php:181
767
  msgid ""
768
  "Application ID for the Sandbox Application, see the details in the My "
769
  "Applications section."
770
  msgstr ""
771
 
772
+ #: includes/Settings.php:186
773
  msgid "Sandbox Access Token"
774
  msgstr ""
775
 
776
+ #: includes/Settings.php:188
777
  msgid ""
778
  "Access Token for the Sandbox Test Account, see the details in the Sandbox "
779
  "Test Account section. Make sure you use the correct Sandbox Access Token "
781
  "Application is assigned a different Access Token."
782
  msgstr ""
783
 
784
+ #: includes/Settings.php:195
785
  msgid "Business location"
786
  msgstr ""
787
 
788
+ #: includes/Settings.php:200
789
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
790
  #. <a> tag, %4$s - </a> tag
791
  msgid ""
794
  "linked."
795
  msgstr ""
796
 
797
+ #: includes/Settings.php:210
798
  msgid "Product system of record"
799
  msgstr ""
800
 
801
+ #: includes/Settings.php:215
802
  #. translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s -
803
  #. <a> tag, %4$s - </a> tag
804
  msgid ""
807
  "here%4$s to read more about choosing a system of record."
808
  msgstr ""
809
 
810
+ #: includes/Settings.php:222
811
  msgid "Do not sync product data"
812
  msgstr ""
813
 
815
  msgid "WooCommerce"
816
  msgstr ""
817
 
818
+ #: includes/Settings.php:230
819
  msgid "Sync inventory"
820
  msgstr ""
821
 
822
+ #: includes/Settings.php:231
823
  msgid "Enable to sync product inventory with Square"
824
  msgstr ""
825
 
826
+ #: includes/Settings.php:237
827
  msgid "Handle missing products"
828
  msgstr ""
829
 
830
+ #: includes/Settings.php:238
831
  msgid "Hide synced products when not found in Square"
832
  msgstr ""
833
 
834
+ #: includes/Settings.php:240
835
  msgid ""
836
  "Products not found in Square will be hidden in the WooCommerce product "
837
  "catalog."
838
  msgstr ""
839
 
840
+ #: includes/Settings.php:246
841
  msgid ""
842
  "Run an import to create new products in this WooCommerce store for each new "
843
  "product created in Square that has a unique SKU not existing in here. Needs "
844
  "to be run each time new items are created in Square."
845
  msgstr ""
846
 
847
+ #: includes/Settings.php:256
848
  msgid "Connection"
849
  msgstr ""
850
 
851
+ #: includes/Settings.php:273
852
  #. translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag
853
  msgid "Log debug messages to the %1$sWooCommerce status log%2$s"
854
  msgstr ""
855
 
856
+ #: includes/Settings.php:321
857
  msgid "Please choose a location"
858
  msgstr ""
859
 
860
+ #: includes/Settings.php:355
861
  msgid "Import all products from Square"
862
  msgstr ""
863
 
864
+ #: includes/Sync/Interval_Polling.php:100
865
  msgid "Updated data for %d category."
866
  msgid_plural "Updated data for %d categories."
867
  msgstr[0] ""
868
  msgstr[1] ""
869
 
870
+ #: includes/Sync/Manual_Synchronization.php:1135
871
  #. translators: Placeholder: %s - product ID
872
  msgid "Product %s could not be updated in Square."
873
  msgstr ""
874
 
875
+ #: includes/Sync/Product_Import.php:521
876
+ #. translators: Placeholders: %1$s - Square item name, %2$s - Square item
877
+ #. variation name, %3$s - failure reason
878
+ msgid "Could not import \"%1$s - %2$s\" from Square. %3$s"
 
 
 
 
 
 
879
  msgstr ""
880
 
881
+ #: includes/Sync/Product_Import.php:572
882
+ msgid "Items with variable pricing cannot be imported."
883
  msgstr ""
884
 
885
+ #: includes/Sync/Product_Import.php:665 includes/Sync/Product_Import.php:936
886
+ msgid "The SKU already exists on another product"
 
887
  msgstr ""
888
 
889
+ #: includes/Sync/Product_Import.php:852
890
+ #. translators: Placeholders: %1$s - variation ID, %2$s - product name
891
+ msgid "Variation #%1$s of %2$s"
892
  msgstr ""
893
 
894
+ #: includes/Sync/Product_Import.php:1230
895
+ #. translators: Placeholders: %s - missing parameter name
896
+ msgid "Missing parameter %s"
 
897
  msgstr ""
898
 
899
+ #: includes/Sync/Product_Import.php:1236
900
+ #. translators: Placeholders: %s - comma separated list of valid product types
901
+ msgid "Invalid product type - the product type must be any of these: %s"
902
  msgstr ""
903
 
904
+ #: includes/Sync/Product_Import.php:1287
905
+ #. translators: Placeholders: %1$s - Square item name, %2$s - Failure reason
906
+ msgid "Could not update %1$s from Square. %2$s"
907
  msgstr ""
908
 
909
+ #: includes/Sync/Product_Import.php:1290
910
+ #. translators: Placeholders: %1$s - Square item name, %2$s - Failure reason
911
+ msgid "Could not import %1$s from Square. %2$s"
912
  msgstr ""
913
 
914
+ #: includes/Sync/Records/Record.php:206
915
  msgid "Info"
916
  msgstr ""
917
 
918
+ #: includes/Sync/Records/Record.php:207
919
  msgid "Notice"
920
  msgstr ""
921
 
922
+ #: includes/Sync/Records/Record.php:208
923
  msgid "Alert"
924
  msgstr ""
925
 
926
+ #: includes/Sync/Records/Record.php:374
927
  #. translators: Placeholder: %s - product name
928
  msgid "%s not found in Square."
929
  msgstr ""
930
 
931
+ #: includes/Sync/Records/Record.php:545
932
  #: vendor/prospress/action-scheduler/classes/ActionScheduler_ListTable.php:92
933
  #: vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/class-sv-wc-payment-gateway-my-payment-methods.php:819
934
  msgid "Delete"
935
  msgstr ""
936
 
937
+ #: includes/Sync/Records/Record.php:553
938
  msgid "Ignore"
939
  msgstr ""
940
 
941
+ #: includes/Sync/Records/Record.php:562
942
  msgid "Unlink"
943
  msgstr ""
944
 
946
  msgid "Encryption is not supported on this site."
947
  msgstr ""
948
 
949
+ #: includes/Utilities/Encryption_Utility.php:73
950
  msgid "%1$s encryption is not available on this site. %2$s will be used instead."
951
  msgstr ""
952
 
953
+ #: includes/Utilities/Encryption_Utility.php:98
954
  msgid "No encryption method available"
955
  msgstr ""
956
 
957
+ #: includes/Utilities/Encryption_Utility.php:102
958
  msgid "Data must be a non-empty string or array"
959
  msgstr ""
960
 
961
+ #: includes/Utilities/Encryption_Utility.php:106
962
+ #: includes/Utilities/Encryption_Utility.php:149
963
  msgid "Encryption key must be a string"
964
  msgstr ""
965
 
966
+ #: includes/Utilities/Encryption_Utility.php:118
967
  msgid "Could not generate encryption vector."
968
  msgstr ""
969
 
970
+ #: includes/Utilities/Encryption_Utility.php:141
971
  msgid "No decryption method available"
972
  msgstr ""
973
 
974
+ #: includes/Utilities/Encryption_Utility.php:145
975
  msgid "Data must be a non-empty string"
976
  msgstr ""
977
 
1363
  msgid "Action Group"
1364
  msgstr ""
1365
 
 
 
 
 
 
 
 
 
1366
  #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:376
1367
  msgid "Cowabunga"
1368
  msgstr ""
1369
 
 
 
 
 
1370
  #: vendor/skyverge/wc-plugin-framework/woocommerce/Lifecycle.php:378
1371
  msgid "Hot dog"
1372
  msgstr ""
1373
 
 
 
 
 
 
 
 
 
 
1374
  #: vendor/skyverge/wc-plugin-framework/woocommerce/admin/abstract-sv-wc-plugin-admin-setup-wizard.php:183
1375
  msgid ""
1376
  "Thanks for installing %1$s! To get started, take a minute to %2$sread the "
3050
  msgid "https://www.woocommerce.com/"
3051
  msgstr ""
3052
 
3053
+ #: includes/Admin/Sync_Page.php:287
3054
  msgctxt "Delete all records"
3055
  msgid "Clear history"
3056
  msgstr ""
3057
 
3058
+ #: includes/Admin/Sync_Page.php:292 includes/Admin/Sync_Page.php:333
3059
  msgctxt "Date - Time"
3060
  msgid "Time"
3061
  msgstr ""
includes/AJAX.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
@@ -46,21 +46,21 @@ class AJAX {
46
  public function __construct() {
47
 
48
  // check an individual product sync status
49
- add_action( 'wp_ajax_wc_square_get_quick_edit_product_details', [ $this, 'get_quick_edit_product_details' ] );
50
 
51
  // fetch product stock from Square
52
- add_action( 'wp_ajax_wc_square_fetch_product_stock_with_square', [ $this, 'fetch_product_stock_with_square' ] );
53
 
54
- add_action( 'wp_ajax_wc_square_import_products_from_square', [ $this, 'import_products_from_square' ] );
55
 
56
  // sync all products with Square
57
- add_action( 'wp_ajax_wc_square_sync_products_with_square', [ $this, 'sync_products_with_square' ] );
58
 
59
  // handle sync records
60
- add_action( 'wp_ajax_wc_square_handle_sync_records', [ $this, 'handle_sync_records' ] );
61
 
62
  // get the status of a sync job
63
- add_action( 'wp_ajax_wc_square_get_sync_with_square_status', [ $this, 'get_sync_with_square_job_status' ] );
64
  }
65
 
66
 
@@ -75,7 +75,7 @@ class AJAX {
75
  */
76
  public function is_product_synced_with_square() {
77
  _deprecated_function( 'is_product_synced_with_square', '2.1.6', 'get_quick_edit_product_details' );
78
-
79
  check_ajax_referer( 'is-product-synced-with-square', 'security' );
80
 
81
  if ( isset( $_POST['product_id'] ) && ( $product = wc_get_product( $_POST['product_id'] ) ) ) {
@@ -83,7 +83,7 @@ class AJAX {
83
  if ( $product->is_type( 'variable' ) && $product->has_child() ) {
84
  if ( Product::has_multiple_variation_attributes( $product ) ) {
85
  wp_send_json_error( 'multiple_attributes' );
86
- } else if ( ! Product::has_sku( $product ) ) {
87
  wp_send_json_error( 'missing_variation_sku' );
88
  }
89
  } else {
@@ -200,26 +200,23 @@ class AJAX {
200
 
201
  switch ( $action ) {
202
 
203
- case 'delete' :
204
-
205
  $outcome = Records::delete_record( $id );
206
  $error = esc_html__( 'Could not delete record.', 'woocommerce-square' );
207
 
208
- break;
209
-
210
- case 'resolve' :
211
 
212
- if ( $record = Records::get_record( $id) ) {
 
213
  $record->resolve();
214
  $outcome = $record->save();
215
  }
216
 
217
  $error = esc_html__( 'Could not resolve record.', 'woocommerce-square' );
218
 
219
- break;
220
-
221
- case 'unsync' :
222
 
 
223
  $record = Records::get_record( $id );
224
 
225
  if ( $record && ( $product = $record->get_product() ) ) {
@@ -229,7 +226,7 @@ class AJAX {
229
 
230
  $error = esc_html__( 'Could not unsync product.', 'woocommerce-square' );
231
 
232
- break;
233
  }
234
  }
235
 
@@ -264,19 +261,19 @@ class AJAX {
264
 
265
  if ( $job_in_progress = $handler->get_job( $job_id ) ) {
266
 
267
- $result = [
268
- 'action' => $job_in_progress->action,
269
- 'id' => $job_in_progress->id,
270
- 'job_products_count' => count( $job_in_progress->product_ids ),
271
- 'percentage' => ( (float) count( $job_in_progress->processed_product_ids ) / max( 1, count( $job_in_progress->product_ids ) ) ) * 100,
272
- 'processed_products_count' => count( $job_in_progress->processed_product_ids ),
273
- 'skipped_products_count' => count( $job_in_progress->skipped_products ),
274
- 'status' => $job_in_progress->status,
275
- ];
 
276
 
277
  wp_send_json_success( $result );
278
  }
279
-
280
  } catch ( \Exception $e ) {
281
 
282
  wp_send_json_error( $e->getMessage() );
@@ -298,7 +295,7 @@ class AJAX {
298
 
299
  check_ajax_referer( 'get-quick-edit-product-details', 'security' );
300
 
301
- if ( isset( $_POST[ 'product_id' ] ) && ( $product = wc_get_product( $_POST[ 'product_id' ] ) ) ) {
302
 
303
  $is_variable = $product->is_type( 'variable' );
304
 
@@ -317,14 +314,16 @@ class AJAX {
317
  }
318
 
319
  $is_synced_with_square = Product::is_synced_with_square( $product ) ? 'yes' : 'no';
320
- $is_woocommerce_sor = wc_square()->get_settings_handler()->is_system_of_record_woocommerce();
321
-
322
- wp_send_json_success( [
323
- 'edit_url' => $is_woocommerce_sor ? get_edit_post_link( $_POST[ 'product_id' ] ) : null,
324
- 'i18n' => $is_woocommerce_sor ? __( 'Stock must be fetched from Square before editing stock quantity', 'woocommerce-square' ) : null,
325
- 'is_synced_with_square' => $is_synced_with_square,
326
- 'is_variable' => $is_variable,
327
- ] );
 
 
328
  }
329
  wp_send_json_error( 'invalid_product' );
330
  }
23
 
24
  namespace WooCommerce\Square;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
46
  public function __construct() {
47
 
48
  // check an individual product sync status
49
+ add_action( 'wp_ajax_wc_square_get_quick_edit_product_details', array( $this, 'get_quick_edit_product_details' ) );
50
 
51
  // fetch product stock from Square
52
+ add_action( 'wp_ajax_wc_square_fetch_product_stock_with_square', array( $this, 'fetch_product_stock_with_square' ) );
53
 
54
+ add_action( 'wp_ajax_wc_square_import_products_from_square', array( $this, 'import_products_from_square' ) );
55
 
56
  // sync all products with Square
57
+ add_action( 'wp_ajax_wc_square_sync_products_with_square', array( $this, 'sync_products_with_square' ) );
58
 
59
  // handle sync records
60
+ add_action( 'wp_ajax_wc_square_handle_sync_records', array( $this, 'handle_sync_records' ) );
61
 
62
  // get the status of a sync job
63
+ add_action( 'wp_ajax_wc_square_get_sync_with_square_status', array( $this, 'get_sync_with_square_job_status' ) );
64
  }
65
 
66
 
75
  */
76
  public function is_product_synced_with_square() {
77
  _deprecated_function( 'is_product_synced_with_square', '2.1.6', 'get_quick_edit_product_details' );
78
+
79
  check_ajax_referer( 'is-product-synced-with-square', 'security' );
80
 
81
  if ( isset( $_POST['product_id'] ) && ( $product = wc_get_product( $_POST['product_id'] ) ) ) {
83
  if ( $product->is_type( 'variable' ) && $product->has_child() ) {
84
  if ( Product::has_multiple_variation_attributes( $product ) ) {
85
  wp_send_json_error( 'multiple_attributes' );
86
+ } elseif ( ! Product::has_sku( $product ) ) {
87
  wp_send_json_error( 'missing_variation_sku' );
88
  }
89
  } else {
200
 
201
  switch ( $action ) {
202
 
203
+ case 'delete':
 
204
  $outcome = Records::delete_record( $id );
205
  $error = esc_html__( 'Could not delete record.', 'woocommerce-square' );
206
 
207
+ break;
 
 
208
 
209
+ case 'resolve':
210
+ if ( $record = Records::get_record( $id ) ) {
211
  $record->resolve();
212
  $outcome = $record->save();
213
  }
214
 
215
  $error = esc_html__( 'Could not resolve record.', 'woocommerce-square' );
216
 
217
+ break;
 
 
218
 
219
+ case 'unsync':
220
  $record = Records::get_record( $id );
221
 
222
  if ( $record && ( $product = $record->get_product() ) ) {
226
 
227
  $error = esc_html__( 'Could not unsync product.', 'woocommerce-square' );
228
 
229
+ break;
230
  }
231
  }
232
 
261
 
262
  if ( $job_in_progress = $handler->get_job( $job_id ) ) {
263
 
264
+ $result = array(
265
+ 'action' => $job_in_progress->action,
266
+ 'id' => $job_in_progress->id,
267
+ 'job_products_count' => count( $job_in_progress->product_ids ),
268
+ 'percentage' => ( (float) count( $job_in_progress->processed_product_ids ) / max( 1, count( $job_in_progress->product_ids ) ) ) * 100,
269
+ 'imported_products_count' => count( $job_in_progress->processed_product_ids ),
270
+ 'updated_products_count' => count( $job_in_progress->updated_product_ids ),
271
+ 'skipped_products_count' => count( $job_in_progress->skipped_products ),
272
+ 'status' => $job_in_progress->status,
273
+ );
274
 
275
  wp_send_json_success( $result );
276
  }
 
277
  } catch ( \Exception $e ) {
278
 
279
  wp_send_json_error( $e->getMessage() );
295
 
296
  check_ajax_referer( 'get-quick-edit-product-details', 'security' );
297
 
298
+ if ( isset( $_POST['product_id'] ) && ( $product = wc_get_product( $_POST['product_id'] ) ) ) {
299
 
300
  $is_variable = $product->is_type( 'variable' );
301
 
314
  }
315
 
316
  $is_synced_with_square = Product::is_synced_with_square( $product ) ? 'yes' : 'no';
317
+ $is_woocommerce_sor = wc_square()->get_settings_handler()->is_system_of_record_woocommerce();
318
+
319
+ wp_send_json_success(
320
+ array(
321
+ 'edit_url' => $is_woocommerce_sor ? get_edit_post_link( $_POST['product_id'] ) : null,
322
+ 'i18n' => $is_woocommerce_sor ? __( 'Stock must be fetched from Square before editing stock quantity', 'woocommerce-square' ) : null,
323
+ 'is_synced_with_square' => $is_synced_with_square,
324
+ 'is_variable' => $is_variable,
325
+ )
326
+ );
327
  }
328
  wp_send_json_error( 'invalid_product' );
329
  }
includes/API.php CHANGED
@@ -28,7 +28,7 @@ use SquareConnect;
28
  use WooCommerce\Square\API\Requests;
29
  use WooCommerce\Square\API\Responses;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * WooCommerce Square API class
@@ -173,7 +173,7 @@ class API extends Framework\SV_WC_API_Base {
173
  * @return Responses\Catalog
174
  * @throws Framework\SV_WC_API_Exception
175
  */
176
- public function list_catalog( $cursor = '', $types = [] ) {
177
 
178
  $request = $this->get_catalog_request();
179
  $request->set_list_catalog_data( $cursor, $types );
@@ -210,7 +210,7 @@ class API extends Framework\SV_WC_API_Base {
210
  * @return Responses\Catalog
211
  * @throws Framework\SV_WC_API_Exception
212
  */
213
- public function search_catalog_objects( $args = [] ) {
214
 
215
  $request = $this->get_catalog_request();
216
  $request->set_search_catalog_objects_data( $args );
@@ -230,7 +230,7 @@ class API extends Framework\SV_WC_API_Base {
230
  * @return Responses\Catalog
231
  * @throws Framework\SV_WC_API_Exception
232
  */
233
- public function update_item_modifier_lists( array $item_ids, array $modifier_lists_to_enable = [], array $modifier_lists_to_disable = [] ) {
234
 
235
  $request = $this->get_catalog_request();
236
  $request->set_update_item_modifier_lists_data( $item_ids, $modifier_lists_to_enable, $modifier_lists_to_disable );
@@ -250,7 +250,7 @@ class API extends Framework\SV_WC_API_Base {
250
  * @return Responses\Catalog
251
  * @throws Framework\SV_WC_API_Exception
252
  */
253
- public function update_item_taxes( array $item_ids, array $taxes_to_enable = [], array $taxes_to_disable = [] ) {
254
 
255
  $request = $this->get_catalog_request();
256
  $request->set_update_item_taxes_data( $item_ids, $taxes_to_enable, $taxes_to_disable );
@@ -299,27 +299,27 @@ class API extends Framework\SV_WC_API_Base {
299
 
300
  $image = file_get_contents( $image_path );
301
 
302
- $headers = [
303
  'accept' => 'application/json',
304
  'content-type' => 'multipart/form-data; boundary="boundary"',
305
  'Square-Version' => '2019-05-08',
306
  'Authorization' => 'Bearer ' . wc_square()->get_settings_handler()->get_access_token(),
307
- ];
308
 
309
- $body = '--boundary' . "\r\n";
310
  $body .= 'Content-Disposition: form-data; name="request"' . "\r\n";
311
  $body .= 'Content-Type: application/json' . "\r\n\r\n";
312
 
313
- $request = [
314
  'idempotency_key' => wc_square()->get_idempotency_key(),
315
- 'image' => [
316
  'type' => 'IMAGE',
317
  'id' => '#TEMP_ID',
318
- 'image_data' => [
319
  'caption' => esc_attr( $caption ),
320
- ],
321
- ],
322
- ];
323
 
324
  if ( $square_item_id ) {
325
  $request['object_id'] = $square_item_id;
@@ -337,10 +337,13 @@ class API extends Framework\SV_WC_API_Base {
337
 
338
  $url = $this->client->getConfig()->getHost() . '/v2/catalog/images';
339
 
340
- $response = wp_remote_post( $url, [
341
- 'headers' => $headers,
342
- 'body' => $body,
343
- ] );
 
 
 
344
 
345
  if ( is_wp_error( $response ) ) {
346
  throw new Framework\SV_WC_API_Exception( $response->get_error_message() );
@@ -438,18 +441,25 @@ class API extends Framework\SV_WC_API_Base {
438
 
439
  $change = new SquareConnect\Model\InventoryChange();
440
  $change->setType( 'ADJUSTMENT' );
441
- $change->setAdjustment( new SquareConnect\Model\InventoryAdjustment( [
442
- 'catalog_object_id' => $square_id,
443
- 'location_id' => $this->get_plugin()->get_settings_handler()->get_location_id(),
444
- 'quantity' => (string) absint( $amount ),
445
- 'from_state' => $from_state,
446
- 'to_state' => $to_state,
447
- 'occurred_at' => $date->format( DATE_ATOM ),
448
- ] ) );
 
 
 
 
449
 
450
- return $this->batch_change_inventory( uniqid( '', false ), [
451
- $change,
452
- ] );
 
 
 
453
  }
454
 
455
 
@@ -483,7 +493,7 @@ class API extends Framework\SV_WC_API_Base {
483
  * @return Responses\Inventory
484
  * @throws Framework\SV_WC_API_Exception
485
  */
486
- public function batch_retrieve_inventory_changes( array $args = [] ) {
487
 
488
  $request = $this->get_inventory_request();
489
  $request->set_batch_retrieve_inventory_changes_data( $args );
@@ -502,7 +512,7 @@ class API extends Framework\SV_WC_API_Base {
502
  * @return Responses\Inventory
503
  * @throws Framework\SV_WC_API_Exception
504
  */
505
- public function batch_retrieve_inventory_counts( array $args = [] ) {
506
 
507
  $request = $this->get_inventory_request();
508
  $request->set_batch_retrieve_inventory_counts_data( $args );
@@ -561,7 +571,7 @@ class API extends Framework\SV_WC_API_Base {
561
  public function retrieve_inventory_count( $catalog_object_id ) {
562
 
563
  $request = $this->get_inventory_request();
564
- $request->set_retrieve_inventory_count_data( $catalog_object_id, [ $this->get_plugin()->get_settings_handler()->get_location_id() ] );
565
 
566
  return $this->perform_request( $request );
567
  }
@@ -683,12 +693,12 @@ class API extends Framework\SV_WC_API_Base {
683
  case self::REQUEST_TYPE_CATALOG:
684
  $request = new Requests\Catalog( $this->client );
685
  $response_handler = Responses\Catalog::class;
686
- break;
687
 
688
  case self::REQUEST_TYPE_INVENTORY:
689
  $request = new Requests\Inventory( $this->client );
690
  $response_handler = Responses\Inventory::class;
691
- break;
692
 
693
  default:
694
  throw new Framework\SV_WC_API_Exception( 'Invalid request type.' );
@@ -799,7 +809,7 @@ class API extends Framework\SV_WC_API_Base {
799
  return true;
800
  }
801
 
802
- $errors = [];
803
 
804
  foreach ( $this->get_response()->get_errors() as $error ) {
805
  if ( empty( $error->code ) ) {
@@ -809,7 +819,7 @@ class API extends Framework\SV_WC_API_Base {
809
  $errors[] = trim( "[{$error->code}] {$error->detail}" );
810
 
811
  // Last attempt to refresh access token.
812
- if ( 'ACCESS_TOKEN_EXPIRED' == $error->code ) {
813
  $this->get_plugin()->log( 'Access Token Expired, attempting a refresh.' );
814
  $this->get_plugin()->get_connection_handler()->refresh_connection();
815
 
@@ -823,7 +833,7 @@ class API extends Framework\SV_WC_API_Base {
823
  }
824
 
825
  // if the error indicates that access token is bad, disconnect the plugin to prevent further attempts
826
- if ( in_array( $error->code, [ 'ACCESS_TOKEN_EXPIRED', 'ACCESS_TOKEN_REVOKED' ], true ) ) {
827
  $this->get_plugin()->get_connection_handler()->disconnect();
828
  $this->get_plugin()->log( 'Disconnected due to invalid authorization. Please try connecting again.' );
829
  }
@@ -845,23 +855,22 @@ class API extends Framework\SV_WC_API_Base {
845
  */
846
  protected function do_square_request( $square_api, $method, $args ) {
847
 
848
- if ( ! is_callable( [ $square_api, $method ] ) ) {
849
  throw new Framework\SV_WC_API_Exception( 'Invalid API method' );
850
  }
851
 
852
  try {
853
 
854
  // perform the request
855
- $response = call_user_func_array( [ $square_api, $method ], $args );
856
 
857
  if ( is_array( $response ) && $this->request->get_with_http_info() ) {
858
  $this->response_code = isset( $response[1] ) ? (int) $response[1] : 400;
859
- $this->response_headers = isset( $response[2] ) ? (array) $response[2] : [];
860
- $this->raw_response_body = isset( $response[0] ) ? $response[0] : [];
861
  } else {
862
  $this->raw_response_body = $response;
863
  }
864
-
865
  } catch ( SquareConnect\ApiException $exception ) {
866
 
867
  $this->response_code = $exception->getCode();
28
  use WooCommerce\Square\API\Requests;
29
  use WooCommerce\Square\API\Responses;
30
 
31
+ defined( 'ABSPATH' ) || exit;
32
 
33
  /**
34
  * WooCommerce Square API class
173
  * @return Responses\Catalog
174
  * @throws Framework\SV_WC_API_Exception
175
  */
176
+ public function list_catalog( $cursor = '', $types = array() ) {
177
 
178
  $request = $this->get_catalog_request();
179
  $request->set_list_catalog_data( $cursor, $types );
210
  * @return Responses\Catalog
211
  * @throws Framework\SV_WC_API_Exception
212
  */
213
+ public function search_catalog_objects( $args = array() ) {
214
 
215
  $request = $this->get_catalog_request();
216
  $request->set_search_catalog_objects_data( $args );
230
  * @return Responses\Catalog
231
  * @throws Framework\SV_WC_API_Exception
232
  */
233
+ public function update_item_modifier_lists( array $item_ids, array $modifier_lists_to_enable = array(), array $modifier_lists_to_disable = array() ) {
234
 
235
  $request = $this->get_catalog_request();
236
  $request->set_update_item_modifier_lists_data( $item_ids, $modifier_lists_to_enable, $modifier_lists_to_disable );
250
  * @return Responses\Catalog
251
  * @throws Framework\SV_WC_API_Exception
252
  */
253
+ public function update_item_taxes( array $item_ids, array $taxes_to_enable = array(), array $taxes_to_disable = array() ) {
254
 
255
  $request = $this->get_catalog_request();
256
  $request->set_update_item_taxes_data( $item_ids, $taxes_to_enable, $taxes_to_disable );
299
 
300
  $image = file_get_contents( $image_path );
301
 
302
+ $headers = array(
303
  'accept' => 'application/json',
304
  'content-type' => 'multipart/form-data; boundary="boundary"',
305
  'Square-Version' => '2019-05-08',
306
  'Authorization' => 'Bearer ' . wc_square()->get_settings_handler()->get_access_token(),
307
+ );
308
 
309
+ $body = '--boundary' . "\r\n";
310
  $body .= 'Content-Disposition: form-data; name="request"' . "\r\n";
311
  $body .= 'Content-Type: application/json' . "\r\n\r\n";
312
 
313
+ $request = array(
314
  'idempotency_key' => wc_square()->get_idempotency_key(),
315
+ 'image' => array(
316
  'type' => 'IMAGE',
317
  'id' => '#TEMP_ID',
318
+ 'image_data' => array(
319
  'caption' => esc_attr( $caption ),
320
+ ),
321
+ ),
322
+ );
323
 
324
  if ( $square_item_id ) {
325
  $request['object_id'] = $square_item_id;
337
 
338
  $url = $this->client->getConfig()->getHost() . '/v2/catalog/images';
339
 
340
+ $response = wp_remote_post(
341
+ $url,
342
+ array(
343
+ 'headers' => $headers,
344
+ 'body' => $body,
345
+ )
346
+ );
347
 
348
  if ( is_wp_error( $response ) ) {
349
  throw new Framework\SV_WC_API_Exception( $response->get_error_message() );
441
 
442
  $change = new SquareConnect\Model\InventoryChange();
443
  $change->setType( 'ADJUSTMENT' );
444
+ $change->setAdjustment(
445
+ new SquareConnect\Model\InventoryAdjustment(
446
+ array(
447
+ 'catalog_object_id' => $square_id,
448
+ 'location_id' => $this->get_plugin()->get_settings_handler()->get_location_id(),
449
+ 'quantity' => (string) absint( $amount ),
450
+ 'from_state' => $from_state,
451
+ 'to_state' => $to_state,
452
+ 'occurred_at' => $date->format( DATE_ATOM ),
453
+ )
454
+ )
455
+ );
456
 
457
+ return $this->batch_change_inventory(
458
+ uniqid( '', false ),
459
+ array(
460
+ $change,
461
+ )
462
+ );
463
  }
464
 
465
 
493
  * @return Responses\Inventory
494
  * @throws Framework\SV_WC_API_Exception
495
  */
496
+ public function batch_retrieve_inventory_changes( array $args = array() ) {
497
 
498
  $request = $this->get_inventory_request();
499
  $request->set_batch_retrieve_inventory_changes_data( $args );
512
  * @return Responses\Inventory
513
  * @throws Framework\SV_WC_API_Exception
514
  */
515
+ public function batch_retrieve_inventory_counts( array $args = array() ) {
516
 
517
  $request = $this->get_inventory_request();
518
  $request->set_batch_retrieve_inventory_counts_data( $args );
571
  public function retrieve_inventory_count( $catalog_object_id ) {
572
 
573
  $request = $this->get_inventory_request();
574
+ $request->set_retrieve_inventory_count_data( $catalog_object_id, array( $this->get_plugin()->get_settings_handler()->get_location_id() ) );
575
 
576
  return $this->perform_request( $request );
577
  }
693
  case self::REQUEST_TYPE_CATALOG:
694
  $request = new Requests\Catalog( $this->client );
695
  $response_handler = Responses\Catalog::class;
696
+ break;
697
 
698
  case self::REQUEST_TYPE_INVENTORY:
699
  $request = new Requests\Inventory( $this->client );
700
  $response_handler = Responses\Inventory::class;
701
+ break;
702
 
703
  default:
704
  throw new Framework\SV_WC_API_Exception( 'Invalid request type.' );
809
  return true;
810
  }
811
 
812
+ $errors = array();
813
 
814
  foreach ( $this->get_response()->get_errors() as $error ) {
815
  if ( empty( $error->code ) ) {
819
  $errors[] = trim( "[{$error->code}] {$error->detail}" );
820
 
821
  // Last attempt to refresh access token.
822
+ if ( 'ACCESS_TOKEN_EXPIRED' == $error->code ) {
823
  $this->get_plugin()->log( 'Access Token Expired, attempting a refresh.' );
824
  $this->get_plugin()->get_connection_handler()->refresh_connection();
825
 
833
  }
834
 
835
  // if the error indicates that access token is bad, disconnect the plugin to prevent further attempts
836
+ if ( in_array( $error->code, array( 'ACCESS_TOKEN_EXPIRED', 'ACCESS_TOKEN_REVOKED' ), true ) ) {
837
  $this->get_plugin()->get_connection_handler()->disconnect();
838
  $this->get_plugin()->log( 'Disconnected due to invalid authorization. Please try connecting again.' );
839
  }
855
  */
856
  protected function do_square_request( $square_api, $method, $args ) {
857
 
858
+ if ( ! is_callable( array( $square_api, $method ) ) ) {
859
  throw new Framework\SV_WC_API_Exception( 'Invalid API method' );
860
  }
861
 
862
  try {
863
 
864
  // perform the request
865
+ $response = call_user_func_array( array( $square_api, $method ), $args );
866
 
867
  if ( is_array( $response ) && $this->request->get_with_http_info() ) {
868
  $this->response_code = isset( $response[1] ) ? (int) $response[1] : 400;
869
+ $this->response_headers = isset( $response[2] ) ? (array) $response[2] : array();
870
+ $this->raw_response_body = isset( $response[0] ) ? $response[0] : array();
871
  } else {
872
  $this->raw_response_body = $response;
873
  }
 
874
  } catch ( SquareConnect\ApiException $exception ) {
875
 
876
  $this->response_code = $exception->getCode();
includes/API/Request.php CHANGED
@@ -26,7 +26,7 @@ namespace WooCommerce\Square\API;
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use SquareConnect\Configuration;
28
 
29
- defined( 'ABSPATH' ) or exit;
30
 
31
  /**
32
  * Base WooCommerce Square API request object.
@@ -46,7 +46,7 @@ class Request implements Framework\SV_WC_API_Request {
46
  protected $square_api_method;
47
 
48
  /** @var array arguments for the method call */
49
- protected $square_api_args = [];
50
 
51
  /** @var null|object the square API request model */
52
  protected $square_request;
@@ -168,17 +168,17 @@ class Request implements Framework\SV_WC_API_Request {
168
 
169
  $body = '';
170
 
171
- if ( is_callable( [ $this->square_request, '__toString' ] ) ) {
172
 
173
  $body = $this->square_request->__toString();
174
 
175
  } elseif ( is_array( $this->square_api_args ) ) {
176
 
177
- $body = [];
178
 
179
  foreach ( $this->square_api_args as $key => $arg ) {
180
 
181
- if ( is_callable( [ $arg, '__toString' ] ) ) {
182
  $body[ $key ] = $arg->__toString();
183
  }
184
  }
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use SquareConnect\Configuration;
28
 
29
+ defined( 'ABSPATH' ) || exit;
30
 
31
  /**
32
  * Base WooCommerce Square API request object.
46
  protected $square_api_method;
47
 
48
  /** @var array arguments for the method call */
49
+ protected $square_api_args = array();
50
 
51
  /** @var null|object the square API request model */
52
  protected $square_request;
168
 
169
  $body = '';
170
 
171
+ if ( is_callable( array( $this->square_request, '__toString' ) ) ) {
172
 
173
  $body = $this->square_request->__toString();
174
 
175
  } elseif ( is_array( $this->square_api_args ) ) {
176
 
177
+ $body = array();
178
 
179
  foreach ( $this->square_api_args as $key => $arg ) {
180
 
181
+ if ( is_callable( array( $arg, '__toString' ) ) ) {
182
  $body[ $key ] = $arg->__toString();
183
  }
184
  }
includes/API/Requests/Catalog.php CHANGED
@@ -28,7 +28,7 @@ use SquareConnect\Model as SquareModel;
28
  use SquareConnect\Api\CatalogApi;
29
  use WooCommerce\Square\API\Request;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * WooCommerce Square Catalog API Request class.
@@ -64,7 +64,7 @@ class Catalog extends Request {
64
  public function set_batch_delete_catalog_objects_data( array $object_ids ) {
65
 
66
  $this->square_api_method = 'batchDeleteCatalogObjects';
67
- $this->square_api_args = [ new SquareModel\BatchDeleteCatalogObjectsRequest( [ 'object_ids' => $object_ids ] ) ];
68
  }
69
 
70
 
@@ -82,10 +82,14 @@ class Catalog extends Request {
82
  public function set_batch_retrieve_catalog_objects_data( array $object_ids, $include_related_objects = false ) {
83
 
84
  $this->square_api_method = 'batchRetrieveCatalogObjects';
85
- $this->square_api_args = [ new SquareModel\BatchRetrieveCatalogObjectsRequest( [
86
- 'object_ids' => $object_ids,
87
- 'include_related_objects' => (bool) $include_related_objects
88
- ] ) ];
 
 
 
 
89
  }
90
 
91
 
@@ -103,10 +107,14 @@ class Catalog extends Request {
103
  public function set_batch_upsert_catalog_objects_data( $idempotency_key, array $batches ) {
104
 
105
  $this->square_api_method = 'batchUpsertCatalogObjects';
106
- $this->square_api_args = [ new SquareModel\BatchUpsertCatalogObjectsRequest( [
107
- 'idempotency_key' => $idempotency_key,
108
- 'batches' => $batches,
109
- ] ) ];
 
 
 
 
110
  }
111
 
112
 
@@ -121,7 +129,7 @@ class Catalog extends Request {
121
  * @param string $cursor
122
  * @param array $types
123
  */
124
- public function set_catalog_info_data( $cursor = '', $types = [] ) {
125
 
126
  $this->square_api_method = 'catalogInfo';
127
  }
@@ -140,7 +148,7 @@ class Catalog extends Request {
140
  public function set_delete_catalog_object_data( $object_id ) {
141
 
142
  $this->square_api_method = 'deleteCatalogObject';
143
- $this->square_api_args = [ $object_id ];
144
  }
145
 
146
 
@@ -155,13 +163,13 @@ class Catalog extends Request {
155
  * @param string $cursor (optional) the pagination cursor
156
  * @param array $types (optional) the catalog item types to filter by
157
  */
158
- public function set_list_catalog_data( $cursor = '', $types = [] ) {
159
 
160
  $this->square_api_method = 'listCatalog';
161
- $this->square_api_args = [
162
  'cursor' => $cursor,
163
- 'types' => is_array( $types ) ? implode( ',', array_map( 'strtoupper', $types ) ) : ''
164
- ];
165
  }
166
 
167
 
@@ -179,7 +187,7 @@ class Catalog extends Request {
179
  public function set_retrieve_catalog_object_data( $object_id, $include_related_objects = false ) {
180
 
181
  $this->square_api_method = 'retrieveCatalogObject';
182
- $this->square_api_args = [ $object_id, (bool) $include_related_objects ];
183
  }
184
 
185
 
@@ -193,14 +201,14 @@ class Catalog extends Request {
193
  *
194
  * @param array $args see Square documentation for full list of args allowed
195
  */
196
- public function set_search_catalog_objects_data( array $args = [] ) {
197
 
198
  // convert object types to array
199
  if ( isset( $args['object_types'] ) && ! is_array( $args['object_types'] ) ) {
200
- $args['object_types'] = [ $args['object_types'] ];
201
  }
202
 
203
- $defaults = [
204
  'cursor' => null,
205
  'object_types' => null,
206
  'include_deleted_objects' => null,
@@ -208,13 +216,13 @@ class Catalog extends Request {
208
  'begin_time' => null,
209
  'query' => null,
210
  'limit' => null,
211
- ];
212
 
213
  // apply defaults and remove any keys that aren't recognized
214
  $args = array_intersect_key( wp_parse_args( $args, $defaults ), $defaults );
215
 
216
  $this->square_api_method = 'searchCatalogObjects';
217
- $this->square_api_args = [ new SquareModel\SearchCatalogObjectsRequest( $args ) ];
218
  }
219
 
220
 
@@ -230,14 +238,18 @@ class Catalog extends Request {
230
  * @param string[] $modifier_lists_to_enable array of list IDs to enable
231
  * @param string[] $modifier_lists_to_disable array of list IDs to disable
232
  */
233
- public function set_update_item_modifier_lists_data( array $item_ids, array $modifier_lists_to_enable = [], array $modifier_lists_to_disable = [] ) {
234
 
235
  $this->square_api_method = 'updateItemModifierLists';
236
- $this->square_api_args = [ new SquareModel\UpdateItemModifierListsRequest( [
237
- 'item_ids' => $item_ids,
238
- 'modifier_lists_to_enable' => $modifier_lists_to_enable,
239
- 'modifier_lists_to_disable' => $modifier_lists_to_disable,
240
- ] ) ];
 
 
 
 
241
  }
242
 
243
 
@@ -253,14 +265,18 @@ class Catalog extends Request {
253
  * @param string[] $taxes_to_enable array of catalog tax IDs to enable
254
  * @param string[] $taxes_to_disable array of catalog tax IDs to disable
255
  */
256
- public function set_update_item_taxes_data( array $item_ids, array $taxes_to_enable = [], array $taxes_to_disable = [] ) {
257
 
258
  $this->square_api_method = 'updateItemTaxes';
259
- $this->square_api_args = [ new SquareModel\UpdateItemTaxesRequest( [
260
- 'item_ids' => $item_ids,
261
- 'taxes_to_enable' => $taxes_to_enable,
262
- 'taxes_to_disable' => $taxes_to_disable,
263
- ] ) ];
 
 
 
 
264
  }
265
 
266
 
@@ -278,10 +294,14 @@ class Catalog extends Request {
278
  public function set_upsert_catalog_object_data( $idempotency_key, $object ) {
279
 
280
  $this->square_api_method = 'upsertCatalogObject';
281
- $this->square_api_args = [ new SquareModel\UpsertCatalogObjectRequest( [
282
- 'idempotency_key' => $idempotency_key,
283
- 'object' => $object,
284
- ] ) ];
 
 
 
 
285
  }
286
 
287
 
28
  use SquareConnect\Api\CatalogApi;
29
  use WooCommerce\Square\API\Request;
30
 
31
+ defined( 'ABSPATH' ) || exit;
32
 
33
  /**
34
  * WooCommerce Square Catalog API Request class.
64
  public function set_batch_delete_catalog_objects_data( array $object_ids ) {
65
 
66
  $this->square_api_method = 'batchDeleteCatalogObjects';
67
+ $this->square_api_args = array( new SquareModel\BatchDeleteCatalogObjectsRequest( array( 'object_ids' => $object_ids ) ) );
68
  }
69
 
70
 
82
  public function set_batch_retrieve_catalog_objects_data( array $object_ids, $include_related_objects = false ) {
83
 
84
  $this->square_api_method = 'batchRetrieveCatalogObjects';
85
+ $this->square_api_args = array(
86
+ new SquareModel\BatchRetrieveCatalogObjectsRequest(
87
+ array(
88
+ 'object_ids' => $object_ids,
89
+ 'include_related_objects' => (bool) $include_related_objects,
90
+ )
91
+ ),
92
+ );
93
  }
94
 
95
 
107
  public function set_batch_upsert_catalog_objects_data( $idempotency_key, array $batches ) {
108
 
109
  $this->square_api_method = 'batchUpsertCatalogObjects';
110
+ $this->square_api_args = array(
111
+ new SquareModel\BatchUpsertCatalogObjectsRequest(
112
+ array(
113
+ 'idempotency_key' => $idempotency_key,
114
+ 'batches' => $batches,
115
+ )
116
+ ),
117
+ );
118
  }
119
 
120
 
129
  * @param string $cursor
130
  * @param array $types
131
  */
132
+ public function set_catalog_info_data( $cursor = '', $types = array() ) {
133
 
134
  $this->square_api_method = 'catalogInfo';
135
  }
148
  public function set_delete_catalog_object_data( $object_id ) {
149
 
150
  $this->square_api_method = 'deleteCatalogObject';
151
+ $this->square_api_args = array( $object_id );
152
  }
153
 
154
 
163
  * @param string $cursor (optional) the pagination cursor
164
  * @param array $types (optional) the catalog item types to filter by
165
  */
166
+ public function set_list_catalog_data( $cursor = '', $types = array() ) {
167
 
168
  $this->square_api_method = 'listCatalog';
169
+ $this->square_api_args = array(
170
  'cursor' => $cursor,
171
+ 'types' => is_array( $types ) ? implode( ',', array_map( 'strtoupper', $types ) ) : '',
172
+ );
173
  }
174
 
175
 
187
  public function set_retrieve_catalog_object_data( $object_id, $include_related_objects = false ) {
188
 
189
  $this->square_api_method = 'retrieveCatalogObject';
190
+ $this->square_api_args = array( $object_id, (bool) $include_related_objects );
191
  }
192
 
193
 
201
  *
202
  * @param array $args see Square documentation for full list of args allowed
203
  */
204
+ public function set_search_catalog_objects_data( array $args = array() ) {
205
 
206
  // convert object types to array
207
  if ( isset( $args['object_types'] ) && ! is_array( $args['object_types'] ) ) {
208
+ $args['object_types'] = array( $args['object_types'] );
209
  }
210
 
211
+ $defaults = array(
212
  'cursor' => null,
213
  'object_types' => null,
214
  'include_deleted_objects' => null,
216
  'begin_time' => null,
217
  'query' => null,
218
  'limit' => null,
219
+ );
220
 
221
  // apply defaults and remove any keys that aren't recognized
222
  $args = array_intersect_key( wp_parse_args( $args, $defaults ), $defaults );
223
 
224
  $this->square_api_method = 'searchCatalogObjects';
225
+ $this->square_api_args = array( new SquareModel\SearchCatalogObjectsRequest( $args ) );
226
  }
227
 
228
 
238
  * @param string[] $modifier_lists_to_enable array of list IDs to enable
239
  * @param string[] $modifier_lists_to_disable array of list IDs to disable
240
  */
241
+ public function set_update_item_modifier_lists_data( array $item_ids, array $modifier_lists_to_enable = array(), array $modifier_lists_to_disable = array() ) {
242
 
243
  $this->square_api_method = 'updateItemModifierLists';
244
+ $this->square_api_args = array(
245
+ new SquareModel\UpdateItemModifierListsRequest(
246
+ array(
247
+ 'item_ids' => $item_ids,
248
+ 'modifier_lists_to_enable' => $modifier_lists_to_enable,
249
+ 'modifier_lists_to_disable' => $modifier_lists_to_disable,
250
+ )
251
+ ),
252
+ );
253
  }
254
 
255
 
265
  * @param string[] $taxes_to_enable array of catalog tax IDs to enable
266
  * @param string[] $taxes_to_disable array of catalog tax IDs to disable
267
  */
268
+ public function set_update_item_taxes_data( array $item_ids, array $taxes_to_enable = array(), array $taxes_to_disable = array() ) {
269
 
270
  $this->square_api_method = 'updateItemTaxes';
271
+ $this->square_api_args = array(
272
+ new SquareModel\UpdateItemTaxesRequest(
273
+ array(
274
+ 'item_ids' => $item_ids,
275
+ 'taxes_to_enable' => $taxes_to_enable,
276
+ 'taxes_to_disable' => $taxes_to_disable,
277
+ )
278
+ ),
279
+ );
280
  }
281
 
282
 
294
  public function set_upsert_catalog_object_data( $idempotency_key, $object ) {
295
 
296
  $this->square_api_method = 'upsertCatalogObject';
297
+ $this->square_api_args = array(
298
+ new SquareModel\UpsertCatalogObjectRequest(
299
+ array(
300
+ 'idempotency_key' => $idempotency_key,
301
+ 'object' => $object,
302
+ )
303
+ ),
304
+ );
305
  }
306
 
307
 
includes/API/Requests/Customers.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\API\Requests;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SquareConnect\Api\CustomersApi;
29
  use WooCommerce\Square\API;
@@ -60,7 +60,7 @@ class Customers extends API\Request {
60
 
61
  $this->square_api_method = 'retrieveCustomer';
62
 
63
- $this->square_api_args = [ $customer_id ];
64
  }
65
 
66
 
@@ -76,7 +76,7 @@ class Customers extends API\Request {
76
  $this->square_api_method = 'listCustomers';
77
 
78
  if ( $cursor ) {
79
- $this->square_api_args = [ 'cursor' => $cursor ];
80
  }
81
  }
82
 
23
 
24
  namespace WooCommerce\Square\API\Requests;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SquareConnect\Api\CustomersApi;
29
  use WooCommerce\Square\API;
60
 
61
  $this->square_api_method = 'retrieveCustomer';
62
 
63
+ $this->square_api_args = array( $customer_id );
64
  }
65
 
66
 
76
  $this->square_api_method = 'listCustomers';
77
 
78
  if ( $cursor ) {
79
+ $this->square_api_args = array( 'cursor' => $cursor );
80
  }
81
  }
82
 
includes/API/Requests/Inventory.php CHANGED
@@ -28,7 +28,7 @@ use SquareConnect\Model as SquareModel;
28
  use SquareConnect\Api\InventoryApi;
29
  use WooCommerce\Square\API\Request;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * WooCommerce Square Inventory API Request class
@@ -66,11 +66,15 @@ class Inventory extends Request {
66
  public function set_batch_change_inventory_data( $idempotency_key, $changes, $ignore_unchanged_counts = true ) {
67
 
68
  $this->square_api_method = 'batchChangeInventory';
69
- $this->square_api_args = [ new SquareModel\BatchChangeInventoryRequest( [
70
- 'idempotency_key' => $idempotency_key,
71
- 'changes' => $changes,
72
- 'ignore_unchanged_counts' => (bool) $ignore_unchanged_counts
73
- ] ) ];
 
 
 
 
74
  }
75
 
76
 
@@ -91,9 +95,9 @@ class Inventory extends Request {
91
  * @type string $updated_before filters any changes updated before this time
92
  * @type string $cursor pagination cursor
93
  */
94
- public function set_batch_retrieve_inventory_changes_data( $args = [] ) {
95
 
96
- $defaults = [
97
  'catalog_object_ids' => null,
98
  'location_ids' => null,
99
  'types' => null,
@@ -101,13 +105,13 @@ class Inventory extends Request {
101
  'updated_after' => null,
102
  'updated_before' => null,
103
  'cursor' => null,
104
- ];
105
 
106
  // apply defaults and remove any keys that aren't recognized
107
  $args = array_intersect_key( wp_parse_args( $args, $defaults ), $defaults );
108
 
109
  $this->square_api_method = 'batchRetrieveInventoryChanges';
110
- $this->square_api_args = [ new SquareModel\BatchRetrieveInventoryChangesRequest( $args ) ];
111
  }
112
 
113
 
@@ -125,20 +129,20 @@ class Inventory extends Request {
125
  * @type string $updated_after filters any changes updated after this time
126
  * @type string $cursor pagination cursor
127
  */
128
- public function set_batch_retrieve_inventory_counts_data( $args = [] ) {
129
 
130
- $defaults = [
131
  'catalog_object_ids' => null,
132
  'location_ids' => null,
133
  'updated_after' => null,
134
  'cursor' => null,
135
- ];
136
 
137
  // apply defaults and remove any keys that aren't recognized
138
  $args = array_intersect_key( wp_parse_args( $args, $defaults ), $defaults );
139
 
140
  $this->square_api_method = 'batchRetrieveInventoryCounts';
141
- $this->square_api_args = [ new SquareModel\BatchRetrieveInventoryCountsRequest( $args ) ];
142
  }
143
 
144
 
@@ -155,7 +159,7 @@ class Inventory extends Request {
155
  public function set_retrieve_inventory_adjustment_data( $adjustment_id ) {
156
 
157
  $this->square_api_method = 'retrieveInventoryAdjustment';
158
- $this->square_api_args = [ $adjustment_id ];
159
  }
160
 
161
 
@@ -172,7 +176,7 @@ class Inventory extends Request {
172
  public function set_retrieve_inventory_changes_data( $catalog_object_id ) {
173
 
174
  $this->square_api_method = 'retrieveInventoryChanges';
175
- $this->square_api_args = [ $catalog_object_id ];
176
  }
177
 
178
 
@@ -187,10 +191,10 @@ class Inventory extends Request {
187
  * @param string $catalog_object_id the CatalogObject ID to retrieve
188
  * @param string[] $location_ids location IDs
189
  */
190
- public function set_retrieve_inventory_count_data( $catalog_object_id, array $location_ids = [] ) {
191
 
192
  $this->square_api_method = 'retrieveInventoryCount';
193
- $this->square_api_args = [ $catalog_object_id, $location_ids ];
194
  }
195
 
196
 
@@ -207,7 +211,7 @@ class Inventory extends Request {
207
  public function set_retrieve_inventory_physical_count_data( $physical_count_id ) {
208
 
209
  $this->square_api_method = 'retrieveInventoryPhysicalCount';
210
- $this->square_api_args = [ $physical_count_id ];
211
  }
212
 
213
 
28
  use SquareConnect\Api\InventoryApi;
29
  use WooCommerce\Square\API\Request;
30
 
31
+ defined( 'ABSPATH' ) || exit;
32
 
33
  /**
34
  * WooCommerce Square Inventory API Request class
66
  public function set_batch_change_inventory_data( $idempotency_key, $changes, $ignore_unchanged_counts = true ) {
67
 
68
  $this->square_api_method = 'batchChangeInventory';
69
+ $this->square_api_args = array(
70
+ new SquareModel\BatchChangeInventoryRequest(
71
+ array(
72
+ 'idempotency_key' => $idempotency_key,
73
+ 'changes' => $changes,
74
+ 'ignore_unchanged_counts' => (bool) $ignore_unchanged_counts,
75
+ )
76
+ ),
77
+ );
78
  }
79
 
80
 
95
  * @type string $updated_before filters any changes updated before this time
96
  * @type string $cursor pagination cursor
97
  */
98
+ public function set_batch_retrieve_inventory_changes_data( $args = array() ) {
99
 
100
+ $defaults = array(
101
  'catalog_object_ids' => null,
102
  'location_ids' => null,
103
  'types' => null,
105
  'updated_after' => null,
106
  'updated_before' => null,
107
  'cursor' => null,
108
+ );
109
 
110
  // apply defaults and remove any keys that aren't recognized
111
  $args = array_intersect_key( wp_parse_args( $args, $defaults ), $defaults );
112
 
113
  $this->square_api_method = 'batchRetrieveInventoryChanges';
114
+ $this->square_api_args = array( new SquareModel\BatchRetrieveInventoryChangesRequest( $args ) );
115
  }
116
 
117
 
129
  * @type string $updated_after filters any changes updated after this time
130
  * @type string $cursor pagination cursor
131
  */
132
+ public function set_batch_retrieve_inventory_counts_data( $args = array() ) {
133
 
134
+ $defaults = array(
135
  'catalog_object_ids' => null,
136
  'location_ids' => null,
137
  'updated_after' => null,
138
  'cursor' => null,
139
+ );
140
 
141
  // apply defaults and remove any keys that aren't recognized
142
  $args = array_intersect_key( wp_parse_args( $args, $defaults ), $defaults );
143
 
144
  $this->square_api_method = 'batchRetrieveInventoryCounts';
145
+ $this->square_api_args = array( new SquareModel\BatchRetrieveInventoryCountsRequest( $args ) );
146
  }
147
 
148
 
159
  public function set_retrieve_inventory_adjustment_data( $adjustment_id ) {
160
 
161
  $this->square_api_method = 'retrieveInventoryAdjustment';
162
+ $this->square_api_args = array( $adjustment_id );
163
  }
164
 
165
 
176
  public function set_retrieve_inventory_changes_data( $catalog_object_id ) {
177
 
178
  $this->square_api_method = 'retrieveInventoryChanges';
179
+ $this->square_api_args = array( $catalog_object_id );
180
  }
181
 
182
 
191
  * @param string $catalog_object_id the CatalogObject ID to retrieve
192
  * @param string[] $location_ids location IDs
193
  */
194
+ public function set_retrieve_inventory_count_data( $catalog_object_id, array $location_ids = array() ) {
195
 
196
  $this->square_api_method = 'retrieveInventoryCount';
197
+ $this->square_api_args = array( $catalog_object_id, $location_ids );
198
  }
199
 
200
 
211
  public function set_retrieve_inventory_physical_count_data( $physical_count_id ) {
212
 
213
  $this->square_api_method = 'retrieveInventoryPhysicalCount';
214
+ $this->square_api_args = array( $physical_count_id );
215
  }
216
 
217
 
includes/API/Requests/Locations.php CHANGED
@@ -28,7 +28,7 @@ use SquareConnect\Model as SquareModel;
28
  use SquareConnect\Api\LocationsApi;
29
  use WooCommerce\Square\API\Request;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * WooCommerce Square locations API Request class.
28
  use SquareConnect\Api\LocationsApi;
29
  use WooCommerce\Square\API\Request;
30
 
31
+ defined( 'ABSPATH' ) || exit;
32
 
33
  /**
34
  * WooCommerce Square locations API Request class.
includes/API/Response.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\API;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Base WooCommerce Square API response object.
@@ -74,7 +74,7 @@ class Response implements Framework\SV_WC_API_Response {
74
  */
75
  public function get_errors() {
76
 
77
- return ! empty( $this->raw_response_data->errors ) && is_array( $this->raw_response_data->errors ) ? $this->raw_response_data->errors : [];
78
  }
79
 
80
 
@@ -100,7 +100,7 @@ class Response implements Framework\SV_WC_API_Response {
100
  */
101
  public function has_error_code( $error_code ) {
102
 
103
- foreach( $this->get_errors() as $error ) {
104
  if ( $error_code === $error->code ) {
105
  return true;
106
  }
@@ -118,7 +118,7 @@ class Response implements Framework\SV_WC_API_Response {
118
  */
119
  public function to_string() {
120
 
121
- return is_callable( [ $this->get_data(), '__toString' ] ) ? $this->get_data() : '';
122
  }
123
 
124
 
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Base WooCommerce Square API response object.
74
  */
75
  public function get_errors() {
76
 
77
+ return ! empty( $this->raw_response_data->errors ) && is_array( $this->raw_response_data->errors ) ? $this->raw_response_data->errors : array();
78
  }
79
 
80
 
100
  */
101
  public function has_error_code( $error_code ) {
102
 
103
+ foreach ( $this->get_errors() as $error ) {
104
  if ( $error_code === $error->code ) {
105
  return true;
106
  }
118
  */
119
  public function to_string() {
120
 
121
+ return is_callable( array( $this->get_data(), '__toString' ) ) ? $this->get_data() : '';
122
  }
123
 
124
 
includes/API/Responses/Catalog.php CHANGED
@@ -26,7 +26,7 @@ namespace WooCommerce\Square\API\Responses;
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\API\Response;
28
 
29
- defined( 'ABSPATH' ) or exit;
30
 
31
  /**
32
  * Catalog response object
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\API\Response;
28
 
29
+ defined( 'ABSPATH' ) || exit;
30
 
31
  /**
32
  * Catalog response object
includes/API/Responses/Connection_Refresh_Response.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\API\Responses;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
23
 
24
  namespace WooCommerce\Square\API\Responses;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
includes/API/Responses/Inventory.php CHANGED
@@ -27,7 +27,7 @@ use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use SquareConnect\Model\BatchRetrieveInventoryCountsResponse;
28
  use WooCommerce\Square\API\Response;
29
 
30
- defined( 'ABSPATH' ) or exit;
31
 
32
  /**
33
  * Inventory response object
@@ -46,7 +46,7 @@ class Inventory extends Response {
46
  */
47
  public function get_counts() {
48
 
49
- $counts = [];
50
 
51
  if ( $this->get_data() instanceof BatchRetrieveInventoryCountsResponse && is_array( $this->get_data()->getCounts() ) ) {
52
  $counts = $this->get_data()->getCounts();
27
  use SquareConnect\Model\BatchRetrieveInventoryCountsResponse;
28
  use WooCommerce\Square\API\Response;
29
 
30
+ defined( 'ABSPATH' ) || exit;
31
 
32
  /**
33
  * Inventory response object
46
  */
47
  public function get_counts() {
48
 
49
+ $counts = array();
50
 
51
  if ( $this->get_data() instanceof BatchRetrieveInventoryCountsResponse && is_array( $this->get_data()->getCounts() ) ) {
52
  $counts = $this->get_data()->getCounts();
includes/API/Responses/Locations.php CHANGED
@@ -27,7 +27,7 @@ use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use SquareConnect\Model as SquareModel;
28
  use WooCommerce\Square\API\Response;
29
 
30
- defined( 'ABSPATH' ) or exit;
31
 
32
  /**
33
  * Catalog response object
@@ -48,7 +48,7 @@ class Locations extends Response {
48
  */
49
  public function get_locations() {
50
 
51
- return $this->get_data() ? $this->get_data()->getLocations() : [];
52
  }
53
 
54
 
27
  use SquareConnect\Model as SquareModel;
28
  use WooCommerce\Square\API\Response;
29
 
30
+ defined( 'ABSPATH' ) || exit;
31
 
32
  /**
33
  * Catalog response object
48
  */
49
  public function get_locations() {
50
 
51
+ return $this->get_data() ? $this->get_data()->getLocations() : array();
52
  }
53
 
54
 
includes/Admin.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use WooCommerce\Square\Handlers\Product;
29
 
@@ -35,13 +35,25 @@ use WooCommerce\Square\Handlers\Product;
35
  class Admin {
36
 
37
 
38
- /** @var Handlers\Products */
 
 
 
 
39
  private $products_handler;
40
 
41
- /** @var Admin\Privacy */
 
 
 
 
42
  private $privacy_handler;
43
 
44
- /** @var Plugin plugin instance */
 
 
 
 
45
  private $plugin;
46
 
47
 
@@ -50,7 +62,7 @@ class Admin {
50
  *
51
  * @since 2.0.0
52
  *
53
- * @param Plugin $plugin plugin instance
54
  */
55
  public function __construct( Plugin $plugin ) {
56
 
@@ -74,16 +86,24 @@ class Admin {
74
  */
75
  private function add_hooks() {
76
 
77
- // add the settings page
78
- add_filter( 'woocommerce_get_settings_pages', function ( $pages ) {
 
 
79
 
80
- $pages[] = new Admin\Settings_Page( $this->get_plugin()->get_settings_handler() );
81
 
82
- return $pages;
83
- } );
 
84
 
85
- // load admin scripts
86
- add_action( 'admin_enqueue_scripts', function() { $this->load_scripts_styles(); } );
 
 
 
 
 
87
  }
88
 
89
 
@@ -97,74 +117,99 @@ class Admin {
97
 
98
  if ( 'product' === $typenow ) {
99
 
100
- wp_enqueue_script( 'wc-square-admin-products', $this->get_plugin()->get_plugin_url() . '/assets/js/admin/wc-square-admin-products.min.js', [ 'jquery' ], Plugin::VERSION, true );
101
-
102
- wp_localize_script( 'wc-square-admin-products', 'wc_square_admin_products', [
103
-
104
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
105
- 'settings_url' => esc_url( $this->get_plugin()->get_settings_url() ),
106
- 'variable_product_types' => $this->get_variable_product_types(),
107
- 'synced_with_square_taxonomy' => Product::SYNCED_WITH_SQUARE_TAXONOMY,
108
- 'is_product_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_product_sync_enabled(),
109
- 'is_woocommerce_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_woocommerce(),
110
- 'is_square_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_square(),
111
- 'is_inventory_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_inventory_sync_enabled(),
112
- 'get_quick_edit_product_details_nonce' => wp_create_nonce( 'get-quick-edit-product-details' ),
113
- 'fetch_product_stock_with_square_nonce' => wp_create_nonce( 'fetch-product-stock-with-square' ),
114
- 'i18n' => [
115
- 'synced_with_square' => __( 'Synced with Square', 'woocommerce-square' ),
116
- 'managed_by_square' => __( 'Managed by Square', 'woocommerce-square' ),
117
- 'fetch_stock_with_square' => __( 'Fetch stock from Square', 'woocommerce-square' ),
118
- ],
119
-
120
- ] );
 
 
 
 
 
 
 
 
121
 
122
  } elseif ( $this->get_plugin()->is_plugin_settings() ) {
123
 
124
- wp_enqueue_style( 'wc-square-admin', $this->get_plugin()->get_plugin_url() . '/assets/css/admin/wc-square-admin.min.css', [], Plugin::VERSION );
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
- wp_enqueue_script( 'wc-square-admin-settings', $this->get_plugin()->get_plugin_url() . '/assets/js/admin/wc-square-admin-settings.min.js', [ 'jquery', 'jquery-blockui', 'backbone', 'wc-backbone-modal' ], Plugin::VERSION, true );
127
 
128
- if ( $sync_job = $this->get_plugin()->get_sync_handler()->get_job_in_progress() ) {
129
  $existing_sync_id = $sync_job->id;
130
  } else {
131
  $existing_sync_id = false;
132
  }
133
 
134
- wp_localize_script( 'wc-square-admin-settings', 'wc_square_admin_settings', [
135
-
136
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
137
- 'is_product_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_product_sync_enabled(),
138
- 'is_woocommerce_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_woocommerce(),
139
- 'is_square_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_square(),
140
- 'is_inventory_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_inventory_sync_enabled(),
141
- 'existing_sync_job_id' => $existing_sync_id,
142
- 'sync_in_background' => $this->get_plugin()->get_sync_handler()->should_sync_in_background(),
143
- 'import_products_from_square' => wp_create_nonce( 'import-products-from-square' ),
144
- 'sync_products_with_square' => wp_create_nonce( 'sync-products-with-square' ),
145
- 'get_sync_with_square_status_nonce' => wp_create_nonce( 'get-sync-with-square-status' ),
146
- 'handle_sync_with_square_records' => wp_create_nonce( 'handle-sync-with-square-records' ),
147
-
148
- 'i18n' => [
149
- 'resolved' => __( 'Resolved', 'woocommerce-square' ),
150
- 'no_records_found' => __( 'No records found', 'woocommerce-square' ),
151
- 'skipped' => __( 'Skipped', 'woocommerce-square' ),
152
- 'imported' => __( 'Imported', 'woocommerce-square' ),
153
- 'sync_inventory_label' => [
154
- 'square' => __( 'Enable to fetch inventory changes from Square', 'woocommerce-square' ),
155
- 'woocommerce' => __( 'Enable to push inventory changes to Square', 'woocommerce-square' ),
156
- ],
157
- 'sync_inventory_description' => [
158
- 'square' => __( 'Inventory is fetched from Square periodically and updated in WooCommerce', 'woocommerce-square' ),
159
- 'woocommerce' => sprintf(
160
- /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
161
- __( 'Inventory is %1$salways fetched from Square%2$s periodically to account for sales from other channels.', 'woocommerce-square' ),
162
- '<strong>', '</strong>'
163
  ),
164
- ],
165
- ],
166
-
167
- ] );
 
 
 
 
 
 
 
 
168
  }
169
  }
170
 
@@ -185,7 +230,7 @@ class Admin {
185
  *
186
  * @param string[] array of product types
187
  */
188
- return (array) apply_filters( 'wc_square_variable_product_types', [ 'variable', 'variable-subscription' ] );
189
  }
190
 
191
 
23
 
24
  namespace WooCommerce\Square;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use WooCommerce\Square\Handlers\Product;
29
 
35
  class Admin {
36
 
37
 
38
+ /**
39
+ * Product handler.
40
+ *
41
+ * @var Handlers\Products
42
+ */
43
  private $products_handler;
44
 
45
+ /**
46
+ * Privacy handler.
47
+ *
48
+ * @var Admin\Privacy
49
+ */
50
  private $privacy_handler;
51
 
52
+ /**
53
+ * Plugin
54
+ *
55
+ * @var Plugin plugin instance
56
+ */
57
  private $plugin;
58
 
59
 
62
  *
63
  * @since 2.0.0
64
  *
65
+ * @param Plugin $plugin plugin instance.
66
  */
67
  public function __construct( Plugin $plugin ) {
68
 
86
  */
87
  private function add_hooks() {
88
 
89
+ // add the settings page.
90
+ add_filter(
91
+ 'woocommerce_get_settings_pages',
92
+ function ( $pages ) {
93
 
94
+ $pages[] = new Admin\Settings_Page( $this->get_plugin()->get_settings_handler() );
95
 
96
+ return $pages;
97
+ }
98
+ );
99
 
100
+ // load admin scripts.
101
+ add_action(
102
+ 'admin_enqueue_scripts',
103
+ function() {
104
+ $this->load_scripts_styles();
105
+ }
106
+ );
107
  }
108
 
109
 
117
 
118
  if ( 'product' === $typenow ) {
119
 
120
+ wp_enqueue_script(
121
+ 'wc-square-admin-products',
122
+ $this->get_plugin()->get_plugin_url() . '/assets/js/admin/wc-square-admin-products.min.js',
123
+ array( 'jquery' ),
124
+ Plugin::VERSION,
125
+ true
126
+ );
127
+
128
+ wp_localize_script(
129
+ 'wc-square-admin-products',
130
+ 'wc_square_admin_products',
131
+ array(
132
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
133
+ 'settings_url' => esc_url( $this->get_plugin()->get_settings_url() ),
134
+ 'variable_product_types' => $this->get_variable_product_types(),
135
+ 'synced_with_square_taxonomy' => Product::SYNCED_WITH_SQUARE_TAXONOMY,
136
+ 'is_product_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_product_sync_enabled(),
137
+ 'is_woocommerce_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_woocommerce(),
138
+ 'is_square_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_square(),
139
+ 'is_inventory_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_inventory_sync_enabled(),
140
+ 'get_quick_edit_product_details_nonce' => wp_create_nonce( 'get-quick-edit-product-details' ),
141
+ 'fetch_product_stock_with_square_nonce' => wp_create_nonce( 'fetch-product-stock-with-square' ),
142
+ 'i18n' => array(
143
+ 'synced_with_square' => __( 'Synced with Square', 'woocommerce-square' ),
144
+ 'managed_by_square' => __( 'Managed by Square', 'woocommerce-square' ),
145
+ 'fetch_stock_with_square' => __( 'Fetch stock from Square', 'woocommerce-square' ),
146
+ ),
147
+ )
148
+ );
149
 
150
  } elseif ( $this->get_plugin()->is_plugin_settings() ) {
151
 
152
+ wp_enqueue_style(
153
+ 'wc-square-admin',
154
+ $this->get_plugin()->get_plugin_url() . '/assets/css/admin/wc-square-admin.min.css',
155
+ array(),
156
+ Plugin::VERSION
157
+ );
158
+
159
+ wp_enqueue_script(
160
+ 'wc-square-admin-settings',
161
+ $this->get_plugin()->get_plugin_url() . '/assets/js/admin/wc-square-admin-settings.min.js',
162
+ array( 'jquery', 'jquery-blockui', 'backbone', 'wc-backbone-modal' ),
163
+ Plugin::VERSION,
164
+ true
165
+ );
166
 
167
+ $sync_job = $this->get_plugin()->get_sync_handler()->get_job_in_progress();
168
 
169
+ if ( $sync_job ) {
170
  $existing_sync_id = $sync_job->id;
171
  } else {
172
  $existing_sync_id = false;
173
  }
174
 
175
+ wp_localize_script(
176
+ 'wc-square-admin-settings',
177
+ 'wc_square_admin_settings',
178
+ array(
179
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
180
+ 'is_product_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_product_sync_enabled(),
181
+ 'is_woocommerce_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_woocommerce(),
182
+ 'is_square_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_square(),
183
+ 'is_inventory_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_inventory_sync_enabled(),
184
+ 'is_sandbox' => $this->get_plugin()->get_settings_handler()->is_sandbox(),
185
+ 'existing_sync_job_id' => $existing_sync_id,
186
+ 'sync_in_background' => $this->get_plugin()->get_sync_handler()->should_sync_in_background(),
187
+ 'import_products_from_square' => wp_create_nonce( 'import-products-from-square' ),
188
+ 'sync_products_with_square' => wp_create_nonce( 'sync-products-with-square' ),
189
+ 'get_sync_with_square_status_nonce' => wp_create_nonce( 'get-sync-with-square-status' ),
190
+ 'handle_sync_with_square_records' => wp_create_nonce( 'handle-sync-with-square-records' ),
191
+ 'i18n' => array(
192
+ 'resolved' => __( 'Resolved', 'woocommerce-square' ),
193
+ 'no_records_found' => __( 'No records found', 'woocommerce-square' ),
194
+ 'skipped' => __( 'Skipped', 'woocommerce-square' ),
195
+ 'updated' => __( 'Updated', 'woocommerce-square' ),
196
+ 'imported' => __( 'Imported', 'woocommerce-square' ),
197
+ 'sync_inventory_label' => array(
198
+ 'square' => __( 'Enable to fetch inventory changes from Square', 'woocommerce-square' ),
199
+ 'woocommerce' => __( 'Enable to push inventory changes to Square', 'woocommerce-square' ),
 
 
 
 
200
  ),
201
+ 'sync_inventory_description' => array(
202
+ 'square' => __( 'Inventory is fetched from Square periodically and updated in WooCommerce', 'woocommerce-square' ),
203
+ 'woocommerce' => sprintf(
204
+ /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
205
+ __( 'Inventory is %1$salways fetched from Square%2$s periodically to account for sales from other channels.', 'woocommerce-square' ),
206
+ '<strong>',
207
+ '</strong>'
208
+ ),
209
+ ),
210
+ ),
211
+ )
212
+ );
213
  }
214
  }
215
 
230
  *
231
  * @param string[] array of product types
232
  */
233
+ return (array) apply_filters( 'wc_square_variable_product_types', array( 'variable', 'variable-subscription' ) );
234
  }
235
 
236
 
includes/Admin/Privacy.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Admin;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -42,10 +42,9 @@ class Privacy extends \WC_Abstract_Privacy {
42
  */
43
  public function __construct() {
44
 
45
-
46
  parent::__construct( __( 'Square', 'woocommerce-square' ) );
47
 
48
- $this->add_eraser( 'woocommerce-square-customer-data', __( 'WooCommerce Square Customer Data', 'woocommerce-square' ), [ $this, 'customer_data_eraser' ] );
49
  }
50
 
51
 
@@ -60,7 +59,8 @@ class Privacy extends \WC_Abstract_Privacy {
60
  sprintf(
61
  /* translators: Placeholder: %1$s - <a> tag, %2$s - </a> tag */
62
  __( 'By using this extension, you may be storing personal data or sharing data with an external service. %1$sLearn more about how this works, including what you may want to include in your privacy policy.%2$s', 'woocommerce-square' ),
63
- '<a href="https://docs.woocommerce.com/document/privacy-payments/#woocommerce-square" target="_blank">', '</a>'
 
64
  )
65
  );
66
  }
@@ -82,7 +82,7 @@ class Privacy extends \WC_Abstract_Privacy {
82
  $square_customer_id = get_user_meta( $user->ID, 'wc_square_customer_id', true );
83
 
84
  $items_removed = false;
85
- $messages = [];
86
 
87
  if ( ! empty( $square_customer_id ) ) {
88
 
@@ -93,12 +93,12 @@ class Privacy extends \WC_Abstract_Privacy {
93
  $messages[] = __( 'Square User Data Erased.', 'woocommerce-square' );
94
  }
95
 
96
- return [
97
  'items_removed' => $items_removed,
98
  'items_retained' => false,
99
  'messages' => $messages,
100
  'done' => true,
101
- ];
102
  }
103
 
104
 
23
 
24
  namespace WooCommerce\Square\Admin;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
42
  */
43
  public function __construct() {
44
 
 
45
  parent::__construct( __( 'Square', 'woocommerce-square' ) );
46
 
47
+ $this->add_eraser( 'woocommerce-square-customer-data', __( 'WooCommerce Square Customer Data', 'woocommerce-square' ), array( $this, 'customer_data_eraser' ) );
48
  }
49
 
50
 
59
  sprintf(
60
  /* translators: Placeholder: %1$s - <a> tag, %2$s - </a> tag */
61
  __( 'By using this extension, you may be storing personal data or sharing data with an external service. %1$sLearn more about how this works, including what you may want to include in your privacy policy.%2$s', 'woocommerce-square' ),
62
+ '<a href="https://docs.woocommerce.com/document/privacy-payments/#woocommerce-square" target="_blank">',
63
+ '</a>'
64
  )
65
  );
66
  }
82
  $square_customer_id = get_user_meta( $user->ID, 'wc_square_customer_id', true );
83
 
84
  $items_removed = false;
85
+ $messages = array();
86
 
87
  if ( ! empty( $square_customer_id ) ) {
88
 
93
  $messages[] = __( 'Square User Data Erased.', 'woocommerce-square' );
94
  }
95
 
96
+ return array(
97
  'items_removed' => $items_removed,
98
  'items_retained' => false,
99
  'messages' => $messages,
100
  'done' => true,
101
+ );
102
  }
103
 
104
 
includes/Admin/Settings_Page.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Admin;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square;
@@ -141,10 +141,10 @@ class Settings_Page extends \WC_Settings_Page {
141
  */
142
  public function get_sections() {
143
 
144
- $sections = [
145
- '' => __( 'Settings', 'woocommerce-square' ), // this key is intentionally blank
146
- 'update' => __( 'Update', 'woocommerce-square' ),
147
- ];
148
 
149
  /**
150
  * Filters the WooCommerce Square settings sections.
@@ -177,7 +177,7 @@ class Settings_Page extends \WC_Settings_Page {
177
  </header>
178
  <article>
179
  <?php /* translators: Placeholders: %1$s - <strong>, %2%s - </strong> */ ?>
180
- <?php printf( esc_html__( 'You are about to import all products from Square. This will create a new product in WooCommerce for every product retrieved from Square. If you have products in the trash from the previous imports, these will be ignored in the import. %1$sOnly use this action to perform a one-time import!%2$s', 'woocommerce-square' ), '<strong>', '</strong>' ); ?>
181
  </article>
182
  <footer>
183
  <div class="inner">
23
 
24
  namespace WooCommerce\Square\Admin;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square;
141
  */
142
  public function get_sections() {
143
 
144
+ $sections = array(
145
+ '' => __( 'Settings', 'woocommerce-square' ), // this key is intentionally blank
146
+ 'update' => __( 'Update', 'woocommerce-square' ),
147
+ );
148
 
149
  /**
150
  * Filters the WooCommerce Square settings sections.
177
  </header>
178
  <article>
179
  <?php /* translators: Placeholders: %1$s - <strong>, %2%s - </strong> */ ?>
180
+ <?php printf( esc_html__( 'You are about to import all new products, variations and categories from Square. This will create a new product in WooCommerce for every product retrieved from Square. If you have products in the trash from the previous imports, these will be ignored in the import. During this process, simple products that have new variations found in Sqaure will be converted to a variable product in WooCommerce. %1$sOnly use this action to perform a one-time import!%2$s', 'woocommerce-square' ), '<strong>', '</strong>' ); ?>
181
  </article>
182
  <footer>
183
  <div class="inner">
includes/Admin/Sync_Page.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Admin;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
@@ -74,20 +74,28 @@ class Sync_Page {
74
 
75
  <tr>
76
  <td>
77
- <?php printf(
 
78
  /* translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s, $4%s - closing </strong> HTML tag */
79
  esc_html__( '%1$sSquare%2$s is the system of record. The following data from Square will overwrite WooCommerce data for synced products: %3$sname, price, description, category, inventory%4$s.', 'woocommerce-square' ),
80
- '<strong>', '</strong>', '<strong>', '</strong>'
81
- ); ?>
 
 
 
 
82
  </td>
83
  </tr>
84
  <tr>
85
  <td>
86
- <?php printf(
 
87
  /* translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing </strong> HTML tag */
88
  esc_html__( '%1$sProduct images%2$s will be imported from Square if no featured image is set in WooCommerce.', 'woocommerce-square' ),
89
- '<strong>', '</strong>'
90
- );?>
 
 
91
  </td>
92
  </tr>
93
 
@@ -95,11 +103,16 @@ class Sync_Page {
95
 
96
  <tr>
97
  <td>
98
- <?php printf(
 
99
  /* translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s, %4$s - closing </strong> HTML tag */
100
  esc_html__( '%1$sWooCommerce%2$s is the system of record. The following data from WooCommerce will overwrite Square data for synced products: %3$sname, price, inventory, category, image%4$s.', 'woocommerce-square' ),
101
- '<strong>', '</strong>', '<strong>', '</strong>'
102
- ); ?>
 
 
 
 
103
  </td>
104
  </tr>
105
 
@@ -107,11 +120,14 @@ class Sync_Page {
107
 
108
  <tr>
109
  <td>
110
- <?php printf(
 
111
  /* translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing </strong> HTML tag*/
112
  esc_html__( '%1$sNo chosen system of record.%2$s Products will not be synced between Square and WooCommerce.', 'woocommerce-square' ),
113
- '<strong>', '</strong>'
114
- ); ?>
 
 
115
  </td>
116
  </tr>
117
 
@@ -131,13 +147,13 @@ class Sync_Page {
131
 
132
  $is_connected = wc_square()->get_settings_handler()->is_connected();
133
  $sync_in_progress = $is_connected ? wc_square()->get_sync_handler()->is_sync_in_progress() : false;
134
- $synced_products = wc_square()->get_settings_handler()->is_product_sync_enabled() ? Product::get_products_synced_with_square() : [];
135
  $synced_count = count( $synced_products );
136
  $is_product_import = false;
137
 
138
  if ( $sync_in_progress ) {
139
 
140
- $current_job = wc_square()->get_sync_handler()->get_job_in_progress();
141
  $is_product_import = isset( $current_job->action ) && 'product_import' === $current_job->action;
142
  }
143
 
@@ -170,11 +186,13 @@ class Sync_Page {
170
  <tr>
171
  <td class="synced-products">
172
  <a href="<?php echo esc_url( admin_url( 'edit.php?s&post_status=all&post_type=product&product_type=synced-with-square&stock_status&paged=1' ) ); ?>">
173
- <?php printf(
 
174
  /* translators: Placeholder: %d number of products synced with Square */
175
  _n( '%d product', '%d products', $synced_count, 'woocommerce-square' ),
176
  $synced_count
177
- ); ?>
 
178
  </a>
179
  <input
180
  type="hidden"
@@ -255,12 +273,17 @@ class Sync_Page {
255
  */
256
  private static function output_sync_records() {
257
 
258
- $records = Records::get_records(); ?>
 
259
 
260
  <button
261
  id="wc-square_clear-sync-records"
262
  class="button button-large"
263
- <?php if ( empty( $records ) ) { echo 'disabled="disabled"'; } ?>
 
 
 
 
264
  ><?php echo esc_html_x( 'Clear history', 'Delete all records', 'woocommerce-square' ); ?></button>
265
 
266
  <table class="wc_square_table records widefat" cellspacing="0">
@@ -299,7 +322,7 @@ class Sync_Page {
299
 
300
  <tr>
301
  <td colspan="4">
302
- <em><?php esc_html_e( 'No records found.', 'woocommerce-square' ) ?></em>
303
  </td>
304
  </tr>
305
 
@@ -356,12 +379,14 @@ class Sync_Page {
356
  <?php endif; ?>
357
  </ul>
358
  <?php $additional_info = ob_get_clean(); ?>
359
- <?php printf(
 
360
  /* translators: Placeholders: %1$s - the system of record name (e.g. Square or WooCommerce), %3%s - unordered HTML list of additional information item(s) */
361
  esc_html__( 'You are about to sync products with Square. %1$s is your system of record. For all products synced with Square: %2$s', 'woocommerce-square' ),
362
  $square_settings->get_system_of_record_name(),
363
  $additional_info
364
- ); ?>
 
365
  </article>
366
  <footer>
367
  <div class="inner">
23
 
24
  namespace WooCommerce\Square\Admin;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
74
 
75
  <tr>
76
  <td>
77
+ <?php
78
+ printf(
79
  /* translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s, $4%s - closing </strong> HTML tag */
80
  esc_html__( '%1$sSquare%2$s is the system of record. The following data from Square will overwrite WooCommerce data for synced products: %3$sname, price, description, category, inventory%4$s.', 'woocommerce-square' ),
81
+ '<strong>',
82
+ '</strong>',
83
+ '<strong>',
84
+ '</strong>'
85
+ );
86
+ ?>
87
  </td>
88
  </tr>
89
  <tr>
90
  <td>
91
+ <?php
92
+ printf(
93
  /* translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing </strong> HTML tag */
94
  esc_html__( '%1$sProduct images%2$s will be imported from Square if no featured image is set in WooCommerce.', 'woocommerce-square' ),
95
+ '<strong>',
96
+ '</strong>'
97
+ );
98
+ ?>
99
  </td>
100
  </tr>
101
 
103
 
104
  <tr>
105
  <td>
106
+ <?php
107
+ printf(
108
  /* translators: Placeholders: %1$s, %3$s - opening <strong> HTML tag, %2$s, %4$s - closing </strong> HTML tag */
109
  esc_html__( '%1$sWooCommerce%2$s is the system of record. The following data from WooCommerce will overwrite Square data for synced products: %3$sname, price, inventory, category, image%4$s.', 'woocommerce-square' ),
110
+ '<strong>',
111
+ '</strong>',
112
+ '<strong>',
113
+ '</strong>'
114
+ );
115
+ ?>
116
  </td>
117
  </tr>
118
 
120
 
121
  <tr>
122
  <td>
123
+ <?php
124
+ printf(
125
  /* translators: Placeholders: %1$s - opening <strong> HTML tag, %2$s closing </strong> HTML tag*/
126
  esc_html__( '%1$sNo chosen system of record.%2$s Products will not be synced between Square and WooCommerce.', 'woocommerce-square' ),
127
+ '<strong>',
128
+ '</strong>'
129
+ );
130
+ ?>
131
  </td>
132
  </tr>
133
 
147
 
148
  $is_connected = wc_square()->get_settings_handler()->is_connected();
149
  $sync_in_progress = $is_connected ? wc_square()->get_sync_handler()->is_sync_in_progress() : false;
150
+ $synced_products = wc_square()->get_settings_handler()->is_product_sync_enabled() ? Product::get_products_synced_with_square() : array();
151
  $synced_count = count( $synced_products );
152
  $is_product_import = false;
153
 
154
  if ( $sync_in_progress ) {
155
 
156
+ $current_job = wc_square()->get_sync_handler()->get_job_in_progress();
157
  $is_product_import = isset( $current_job->action ) && 'product_import' === $current_job->action;
158
  }
159
 
186
  <tr>
187
  <td class="synced-products">
188
  <a href="<?php echo esc_url( admin_url( 'edit.php?s&post_status=all&post_type=product&product_type=synced-with-square&stock_status&paged=1' ) ); ?>">
189
+ <?php
190
+ printf(
191
  /* translators: Placeholder: %d number of products synced with Square */
192
  _n( '%d product', '%d products', $synced_count, 'woocommerce-square' ),
193
  $synced_count
194
+ );
195
+ ?>
196
  </a>
197
  <input
198
  type="hidden"
273
  */
274
  private static function output_sync_records() {
275
 
276
+ $records = Records::get_records();
277
+ ?>
278
 
279
  <button
280
  id="wc-square_clear-sync-records"
281
  class="button button-large"
282
+ <?php
283
+ if ( empty( $records ) ) {
284
+ echo 'disabled="disabled"';
285
+ }
286
+ ?>
287
  ><?php echo esc_html_x( 'Clear history', 'Delete all records', 'woocommerce-square' ); ?></button>
288
 
289
  <table class="wc_square_table records widefat" cellspacing="0">
322
 
323
  <tr>
324
  <td colspan="4">
325
+ <em><?php esc_html_e( 'No records found.', 'woocommerce-square' ); ?></em>
326
  </td>
327
  </tr>
328
 
379
  <?php endif; ?>
380
  </ul>
381
  <?php $additional_info = ob_get_clean(); ?>
382
+ <?php
383
+ printf(
384
  /* translators: Placeholders: %1$s - the system of record name (e.g. Square or WooCommerce), %3%s - unordered HTML list of additional information item(s) */
385
  esc_html__( 'You are about to sync products with Square. %1$s is your system of record. For all products synced with Square: %2$s', 'woocommerce-square' ),
386
  $square_settings->get_system_of_record_name(),
387
  $additional_info
388
+ );
389
+ ?>
390
  </article>
391
  <footer>
392
  <div class="inner">
includes/Emails/Access_Token_Email.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\Emails;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Sync completed email.
@@ -44,8 +44,8 @@ class Access_Token_Email extends Base_Email {
44
  $this->customer_email = false;
45
  $this->title = __( 'Square Access Token problems', 'woocommerce-square' );
46
  $this->description = __( 'This email is sent when problems with Access Token are encountered', 'woocommerce-square' );
47
- $this->subject = _x( '[WooCommerce] There was a problem with your Square Access Token', 'Email subject', 'woocommerce-square');
48
- $this->heading = _x( 'There was a problem with your Square Access Token', 'Email heading', 'woocommerce-square');
49
  $this->body = _x( 'Heads up! There may be a problem with your connection to Square.', 'Square connection problems email body.', 'woocommerce-square' );
50
 
51
  $this->enabled_default = 'yes';
@@ -85,7 +85,7 @@ class Access_Token_Email extends Base_Email {
85
  * @param array $args optional associative array with additional arguments
86
  * @return array
87
  */
88
- protected function get_template_args( $args = [] ) {
89
  $html = empty( $args['plain_text'] );
90
 
91
  $email_body = $this->body;
@@ -97,24 +97,29 @@ class Access_Token_Email extends Base_Email {
97
  $email_body .= ' ' . sprintf(
98
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
99
  esc_html__( 'In order to continue accepting payments, please %1$sdisconnect and re-connect your site%2$s.', 'woocommerce-square' ),
100
- '<a href="' . esc_url( $settings_url ) .'">', '</a>'
 
101
  );
102
 
103
  if ( $square->get_settings_handler()->is_debug_enabled() ) {
104
  $email_body .= '<br/><br/>' . sprintf(
105
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
106
  esc_html__( '%1$sInspect status logs%2$s', 'woocommerce-square' ),
107
- '<a href="' . $logs_url . '">', '</a>'
 
108
  );
109
  }
110
  } else {
111
  $email_body .= ' ' + esc_html__( 'In order to continue accepting payments, please disconnect and re-connect your site at ', 'woocommerce-square' ) . $settings_url;
112
  }
113
 
114
- return array_merge( $args, [
115
- 'email' => $this,
116
- 'email_heading' => $this->heading,
117
- 'email_body' => $email_body,
118
- ] );
 
 
 
119
  }
120
  }
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Sync completed email.
44
  $this->customer_email = false;
45
  $this->title = __( 'Square Access Token problems', 'woocommerce-square' );
46
  $this->description = __( 'This email is sent when problems with Access Token are encountered', 'woocommerce-square' );
47
+ $this->subject = _x( '[WooCommerce] There was a problem with your Square Access Token', 'Email subject', 'woocommerce-square' );
48
+ $this->heading = _x( 'There was a problem with your Square Access Token', 'Email heading', 'woocommerce-square' );
49
  $this->body = _x( 'Heads up! There may be a problem with your connection to Square.', 'Square connection problems email body.', 'woocommerce-square' );
50
 
51
  $this->enabled_default = 'yes';
85
  * @param array $args optional associative array with additional arguments
86
  * @return array
87
  */
88
+ protected function get_template_args( $args = array() ) {
89
  $html = empty( $args['plain_text'] );
90
 
91
  $email_body = $this->body;
97
  $email_body .= ' ' . sprintf(
98
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
99
  esc_html__( 'In order to continue accepting payments, please %1$sdisconnect and re-connect your site%2$s.', 'woocommerce-square' ),
100
+ '<a href="' . esc_url( $settings_url ) . '">',
101
+ '</a>'
102
  );
103
 
104
  if ( $square->get_settings_handler()->is_debug_enabled() ) {
105
  $email_body .= '<br/><br/>' . sprintf(
106
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
107
  esc_html__( '%1$sInspect status logs%2$s', 'woocommerce-square' ),
108
+ '<a href="' . $logs_url . '">',
109
+ '</a>'
110
  );
111
  }
112
  } else {
113
  $email_body .= ' ' + esc_html__( 'In order to continue accepting payments, please disconnect and re-connect your site at ', 'woocommerce-square' ) . $settings_url;
114
  }
115
 
116
+ return array_merge(
117
+ $args,
118
+ array(
119
+ 'email' => $this,
120
+ 'email_heading' => $this->heading,
121
+ 'email_body' => $email_body,
122
+ )
123
+ );
124
  }
125
  }
includes/Emails/Base_Email.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\Emails;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Base email class.
@@ -67,7 +67,7 @@ class Base_Email extends \WC_Email {
67
  * @since 2.1.0
68
  */
69
  public function __construct() {
70
- $this->template_base = wc_square()->get_plugin_path() . '/templates/';
71
 
72
  // call parent constructor
73
  parent::__construct();
@@ -106,16 +106,20 @@ class Base_Email extends \WC_Email {
106
  }
107
 
108
  // add a recipient field
109
- $form_fields = Framework\SV_WC_Helper::array_insert_after( $form_fields, isset( $form_fields['enabled'] ) ? 'enabled' : key( $form_fields ), [
110
- 'recipient' => [
111
- 'title' => __( 'Recipient(s)', 'woocommerce-square' ),
112
- 'type' => 'text',
113
- /* translators: Placeholder: %s default email address */
114
- 'description' => sprintf( __( 'Enter recipients (comma separated) for this email. Defaults to admin email: %s', 'woocommerce-square' ), '<code>' . esc_attr( get_option( 'admin_email' ) ) . '</code>' ),
115
- 'placeholder' => get_bloginfo( 'admin_email' ),
116
- 'default' => get_bloginfo( 'admin_email' ),
117
- ],
118
- ] );
 
 
 
 
119
 
120
  // set the updated fields
121
  $this->form_fields = $form_fields;
@@ -162,12 +166,15 @@ class Base_Email extends \WC_Email {
162
  * @param array $args optional associative array with additional arguments
163
  * @return array
164
  */
165
- protected function get_template_args( $args = [] ) {
166
- return array_merge( $args, [
167
- 'email' => $this,
168
- 'email_heading' => '',
169
- 'email_body' => '',
170
- ] );
 
 
 
171
  }
172
 
173
  /**
@@ -178,7 +185,7 @@ class Base_Email extends \WC_Email {
178
  * @return string HTML
179
  */
180
  public function get_content_html() {
181
- $args = [ 'plain_text' => false ];
182
  return wc_get_template_html( $this->template_html, array_merge( $args, $this->get_template_args( $args ) ) );
183
  }
184
 
@@ -190,7 +197,7 @@ class Base_Email extends \WC_Email {
190
  * @return string plain text
191
  */
192
  public function get_content_plain() {
193
- $args = [ 'plain_text' => true ];
194
  return wc_get_template_html( $this->template_plain, array_merge( $args, $this->get_template_args( $args ) ) );
195
  }
196
  }
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Base email class.
67
  * @since 2.1.0
68
  */
69
  public function __construct() {
70
+ $this->template_base = wc_square()->get_plugin_path() . '/templates/';
71
 
72
  // call parent constructor
73
  parent::__construct();
106
  }
107
 
108
  // add a recipient field
109
+ $form_fields = Framework\SV_WC_Helper::array_insert_after(
110
+ $form_fields,
111
+ isset( $form_fields['enabled'] ) ? 'enabled' : key( $form_fields ),
112
+ array(
113
+ 'recipient' => array(
114
+ 'title' => __( 'Recipient(s)', 'woocommerce-square' ),
115
+ 'type' => 'text',
116
+ /* translatorsPlaceholder: %s default email address */
117
+ 'description' => sprintf( __( 'Enter recipients (comma separated) for this email. Defaults to admin email: %s', 'woocommerce-square' ), '<code>' . esc_attr( get_option( 'admin_email' ) ) . '</code>' ),
118
+ 'placeholder' => get_bloginfo( 'admin_email' ),
119
+ 'default' => get_bloginfo( 'admin_email' ),
120
+ ),
121
+ )
122
+ );
123
 
124
  // set the updated fields
125
  $this->form_fields = $form_fields;
166
  * @param array $args optional associative array with additional arguments
167
  * @return array
168
  */
169
+ protected function get_template_args( $args = array() ) {
170
+ return array_merge(
171
+ $args,
172
+ array(
173
+ 'email' => $this,
174
+ 'email_heading' => '',
175
+ 'email_body' => '',
176
+ )
177
+ );
178
  }
179
 
180
  /**
185
  * @return string HTML
186
  */
187
  public function get_content_html() {
188
+ $args = array( 'plain_text' => false );
189
  return wc_get_template_html( $this->template_html, array_merge( $args, $this->get_template_args( $args ) ) );
190
  }
191
 
197
  * @return string plain text
198
  */
199
  public function get_content_plain() {
200
+ $args = array( 'plain_text' => true );
201
  return wc_get_template_html( $this->template_plain, array_merge( $args, $this->get_template_args( $args ) ) );
202
  }
203
  }
includes/Emails/Sync_Completed.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\Emails;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Sync completed email.
@@ -44,8 +44,8 @@ class Sync_Completed extends Base_Email {
44
  $this->customer_email = false;
45
  $this->title = __( 'Square sync completed', 'woocommerce-square' );
46
  $this->description = __( 'This email is sent once a manual sync has been completed between WooCommerce and Square', 'woocommerce-square' );
47
- $this->subject = _x( '[WooCommerce] Square sync completed', 'Email subject', 'woocommerce-square');
48
- $this->heading = _x( 'Square sync completed for {product_count}', 'Email heading with merge tag placeholder', 'woocommerce-square');
49
  $this->body = _x( 'Square sync completed for {site_title} at {sync_completed_date} {sync_completed_time}.', 'Email body with merge tag placeholders', 'woocommerce-square' );
50
 
51
  $this->enabled_default = 'no';
@@ -92,32 +92,35 @@ class Sync_Completed extends Base_Email {
92
  if ( true === $html ) {
93
  $square = wc_square();
94
  $settings_url = $square->get_settings_url();
95
- $records_url = add_query_arg( [ 'section' => 'update' ], $settings_url );
96
 
97
  if ( $square->get_settings_handler()->is_debug_enabled() ) {
98
  $action = sprintf(
99
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
100
  esc_html__( '%1$sInspect status logs%2$s', 'woocommerce-square' ),
101
- '<a href="' . esc_url( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) . '">', '</a>'
 
102
  );
103
  } else {
104
  $action = sprintf(
105
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
106
  esc_html__( '%1$sEnable logging%2$s', 'woocommerce-square' ),
107
- '<a href="' . esc_url( $settings_url ) .'">', '</a>'
 
108
  );
109
  }
110
 
111
  $email_body .= sprintf(
112
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag, %3$s - additional action */
113
  '<br>' . esc_html__( 'The sync job has failed. %1$sClick for more details%2$s, or %3$s.', 'woocommerce-square' ),
114
- '<a href="' . esc_url( $records_url ) .'">', '</a>',
 
115
  strtolower( $action )
116
  );
117
 
118
  } else { // plain text
119
 
120
- if ( wc_square()->get_settings_handler()->is_debug_enabled() ) {
121
  $action = esc_html__( 'Inspect status logs', 'woocommerce-square' );
122
  } else {
123
  $action = esc_html__( 'Enable Logging', 'woocommerce-square' );
@@ -167,7 +170,7 @@ class Sync_Completed extends Base_Email {
167
  if ( $job->manual && ( 'completed' === $job->status || 'failed' === $job->status ) ) {
168
  // for manual jobs, send an email if the job was either completed or failed
169
  $should_send = true;
170
- } else if ( ! $job->manual && 'failed' === $job->status ) {
171
  // for automated jobs, send an email only if the job failed and it's been a day since the last email
172
  $already_sent = get_transient( 'wc_square_failed_sync_email_sent' );
173
  if ( false === $already_sent ) {
@@ -208,26 +211,25 @@ class Sync_Completed extends Base_Email {
208
  if ( isset( $job->completed_at ) ) {
209
  $sync_completed_date = date( wc_date_format(), strtotime( $job->completed_at ) );
210
  $sync_completed_time = date( wc_time_format(), strtotime( $job->completed_at ) );
211
- } else if ( isset( $job->failed_at ) ) {
212
  $sync_completed_date = date( wc_date_format(), strtotime( $job->failed_at ) );
213
  $sync_completed_time = date( wc_time_format(), strtotime( $job->failed_at ) );
214
  }
215
 
216
  // placeholders
217
- $email_merge_tags = [
218
  'product_count' => $product_count,
219
- 'sync_started_date' => isset( $job->started_at ) ? date( wc_date_format(), strtotime( $job->started_at ) ) : '',
220
- 'sync_started_time' => isset( $job->started_at ) ? date( wc_time_format(), strtotime( $job->started_at ) ) : '',
221
  'sync_completed_date' => $sync_completed_date,
222
  'sync_completed_time' => $sync_completed_time,
223
- ];
224
 
225
  // TODO update handling when WooCommerce 3.2 is the minimum required version {FN 2019-05-03}
226
  if ( Framework\SV_WC_Plugin_Compatibility::is_wc_version_gte( '3.2' ) ) {
227
  foreach ( $email_merge_tags as $find => $replace ) {
228
  $this->placeholders[ '{' . $find . '}' ] = $replace;
229
  }
230
-
231
  } else {
232
 
233
  foreach ( $email_merge_tags as $find => $replace ) {
@@ -245,7 +247,7 @@ class Sync_Completed extends Base_Email {
245
  * @param array $args optional associative array with additional arguments
246
  * @return array
247
  */
248
- protected function get_template_args( $args = [] ) {
249
  $sync_job = $this->get_job();
250
  $html = empty( $args['plain_text'] );
251
 
@@ -257,10 +259,13 @@ class Sync_Completed extends Base_Email {
257
  $email_body = $this->get_body_by_job_status( 'completed', $html );
258
  }
259
 
260
- return array_merge( $args, [
261
- 'email' => $this,
262
- 'email_heading' => $email_heading,
263
- 'email_body' => $email_body,
264
- ] );
 
 
 
265
  }
266
  }
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Sync completed email.
44
  $this->customer_email = false;
45
  $this->title = __( 'Square sync completed', 'woocommerce-square' );
46
  $this->description = __( 'This email is sent once a manual sync has been completed between WooCommerce and Square', 'woocommerce-square' );
47
+ $this->subject = _x( '[WooCommerce] Square sync completed', 'Email subject', 'woocommerce-square' );
48
+ $this->heading = _x( 'Square sync completed for {product_count}', 'Email heading with merge tag placeholder', 'woocommerce-square' );
49
  $this->body = _x( 'Square sync completed for {site_title} at {sync_completed_date} {sync_completed_time}.', 'Email body with merge tag placeholders', 'woocommerce-square' );
50
 
51
  $this->enabled_default = 'no';
92
  if ( true === $html ) {
93
  $square = wc_square();
94
  $settings_url = $square->get_settings_url();
95
+ $records_url = add_query_arg( array( 'section' => 'update' ), $settings_url );
96
 
97
  if ( $square->get_settings_handler()->is_debug_enabled() ) {
98
  $action = sprintf(
99
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
100
  esc_html__( '%1$sInspect status logs%2$s', 'woocommerce-square' ),
101
+ '<a href="' . esc_url( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) . '">',
102
+ '</a>'
103
  );
104
  } else {
105
  $action = sprintf(
106
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
107
  esc_html__( '%1$sEnable logging%2$s', 'woocommerce-square' ),
108
+ '<a href="' . esc_url( $settings_url ) . '">',
109
+ '</a>'
110
  );
111
  }
112
 
113
  $email_body .= sprintf(
114
  /* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag, %3$s - additional action */
115
  '<br>' . esc_html__( 'The sync job has failed. %1$sClick for more details%2$s, or %3$s.', 'woocommerce-square' ),
116
+ '<a href="' . esc_url( $records_url ) . '">',
117
+ '</a>',
118
  strtolower( $action )
119
  );
120
 
121
  } else { // plain text
122
 
123
+ if ( wc_square()->get_settings_handler()->is_debug_enabled() ) {
124
  $action = esc_html__( 'Inspect status logs', 'woocommerce-square' );
125
  } else {
126
  $action = esc_html__( 'Enable Logging', 'woocommerce-square' );
170
  if ( $job->manual && ( 'completed' === $job->status || 'failed' === $job->status ) ) {
171
  // for manual jobs, send an email if the job was either completed or failed
172
  $should_send = true;
173
+ } elseif ( ! $job->manual && 'failed' === $job->status ) {
174
  // for automated jobs, send an email only if the job failed and it's been a day since the last email
175
  $already_sent = get_transient( 'wc_square_failed_sync_email_sent' );
176
  if ( false === $already_sent ) {
211
  if ( isset( $job->completed_at ) ) {
212
  $sync_completed_date = date( wc_date_format(), strtotime( $job->completed_at ) );
213
  $sync_completed_time = date( wc_time_format(), strtotime( $job->completed_at ) );
214
+ } elseif ( isset( $job->failed_at ) ) {
215
  $sync_completed_date = date( wc_date_format(), strtotime( $job->failed_at ) );
216
  $sync_completed_time = date( wc_time_format(), strtotime( $job->failed_at ) );
217
  }
218
 
219
  // placeholders
220
+ $email_merge_tags = array(
221
  'product_count' => $product_count,
222
+ 'sync_started_date' => isset( $job->started_at ) ? date( wc_date_format(), strtotime( $job->started_at ) ) : '',
223
+ 'sync_started_time' => isset( $job->started_at ) ? date( wc_time_format(), strtotime( $job->started_at ) ) : '',
224
  'sync_completed_date' => $sync_completed_date,
225
  'sync_completed_time' => $sync_completed_time,
226
+ );
227
 
228
  // TODO update handling when WooCommerce 3.2 is the minimum required version {FN 2019-05-03}
229
  if ( Framework\SV_WC_Plugin_Compatibility::is_wc_version_gte( '3.2' ) ) {
230
  foreach ( $email_merge_tags as $find => $replace ) {
231
  $this->placeholders[ '{' . $find . '}' ] = $replace;
232
  }
 
233
  } else {
234
 
235
  foreach ( $email_merge_tags as $find => $replace ) {
247
  * @param array $args optional associative array with additional arguments
248
  * @return array
249
  */
250
+ protected function get_template_args( $args = array() ) {
251
  $sync_job = $this->get_job();
252
  $html = empty( $args['plain_text'] );
253
 
259
  $email_body = $this->get_body_by_job_status( 'completed', $html );
260
  }
261
 
262
+ return array_merge(
263
+ $args,
264
+ array(
265
+ 'email' => $this,
266
+ 'email_heading' => $email_heading,
267
+ 'email_body' => $email_body,
268
+ )
269
+ );
270
  }
271
  }
includes/Functions.php CHANGED
@@ -21,7 +21,7 @@
21
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
  */
23
 
24
- defined( 'ABSPATH' ) or exit;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
21
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
  */
23
 
24
+ defined( 'ABSPATH' ) || exit;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
includes/Gateway.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model\Customer;
@@ -46,6 +46,15 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
46
  /** @var Gateway\API API base instance */
47
  private $api;
48
 
 
 
 
 
 
 
 
 
 
49
 
50
  /**
51
  * Constructs the class.
@@ -54,39 +63,46 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
54
  */
55
  public function __construct() {
56
 
57
- parent::__construct( Plugin::GATEWAY_ID, wc_square(), [
58
- 'method_title' => __( 'Square', 'woocommerce-square' ),
59
- 'method_description' => __( 'Allow customers to use Square to securely pay with their credit cards', 'woocommerce-square' ),
60
- 'payment_type' => self::PAYMENT_TYPE_CREDIT_CARD,
61
- 'supports' => [
62
- self::FEATURE_PRODUCTS,
63
- self::FEATURE_CARD_TYPES,
64
- self::FEATURE_DETAILED_CUSTOMER_DECLINE_MESSAGES,
65
- self::FEATURE_PAYMENT_FORM,
66
- self::FEATURE_CREDIT_CARD_AUTHORIZATION,
67
- self::FEATURE_CREDIT_CARD_CHARGE,
68
- self::FEATURE_CREDIT_CARD_CHARGE_VIRTUAL,
69
- self::FEATURE_CREDIT_CARD_CAPTURE,
70
- self::FEATURE_REFUNDS,
71
- self::FEATURE_VOIDS,
72
- self::FEATURE_CUSTOMER_ID,
73
- self::FEATURE_TOKENIZATION,
74
- self::FEATURE_ADD_PAYMENT_METHOD,
75
- self::FEATURE_TOKEN_EDITOR,
76
- ],
77
- ] );
 
 
 
 
78
 
79
  $this->view_transaction_url = 'https://squareup.com/dashboard/sales/transactions/%s';
80
 
81
  // log accept.js requests and responses
82
- add_action( 'wp_ajax_wc_' . $this->get_id() . '_log_js_data', [ $this, 'log_js_data' ] );
83
- add_action( 'wp_ajax_nopriv_wc_' . $this->get_id() . '_log_js_data', [ $this, 'log_js_data' ] );
84
 
85
  // store the Square item variation ID to order items
86
- add_action( 'woocommerce_new_order_item', [ $this, 'store_new_order_item_square_meta' ], 10, 3 );
87
 
88
  // restore refunded Square inventory
89
- add_action( 'woocommerce_order_fully_refunded', [ $this, 'restore_refunded_inventory' ], 10, 2 );
 
 
 
90
  }
91
 
92
 
@@ -144,6 +160,20 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
144
  $item->save_meta_data();
145
  }
146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
  /**
149
  * Enqueues the gateway JS.
@@ -151,13 +181,18 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
151
  * @since 2.0.0
152
  */
153
  protected function enqueue_gateway_assets() {
 
 
 
 
 
154
  if ( $this->get_plugin()->get_settings_handler()->is_sandbox() ) {
155
  $url = 'https://js.squareupsandbox.com/v2/paymentform';
156
  } else {
157
  $url = 'https://js.squareup.com/v2/paymentform';
158
  }
159
 
160
- wp_enqueue_script( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-payment-form', $url, [], Plugin::VERSION );
161
 
162
  parent::enqueue_gateway_assets();
163
  }
@@ -187,7 +222,6 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
187
  if ( ! Framework\SV_WC_Helper::get_post( 'wc-' . $this->get_id_dasherized() . '-payment-nonce' ) ) {
188
  throw new Framework\SV_WC_Payment_Gateway_Exception( 'Payment nonce is missing' );
189
  }
190
-
191
  } catch ( Framework\SV_WC_Payment_Gateway_Exception $exception ) {
192
 
193
  $is_valid = false;
@@ -227,8 +261,9 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
227
  $order->payment->exp_year = Framework\SV_WC_Helper::get_post( 'wc-' . $this->get_id_dasherized() . '-exp-year' );
228
  }
229
 
230
- $order->square_order_id = $this->get_order_meta( $order, 'square_order_id' );
231
  $order->square_customer_id = $order->customer_id;
 
 
232
 
233
  // look up in the index for guest customers
234
  if ( ! $order->get_user_id() ) {
@@ -254,7 +289,6 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
254
  if ( ! $order->get_user_id() ) {
255
  Gateway\Customer_Helper::add_customer( $order->square_customer_id, $order->get_billing_email() );
256
  }
257
-
258
  } catch ( \Exception $exception ) {
259
 
260
  // log the error, but continue with payment
@@ -341,6 +375,9 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
341
  if ( $response->get_square_order_id() ) {
342
  $this->update_order_meta( $order, 'square_order_id', $response->get_square_order_id() );
343
  }
 
 
 
344
  }
345
 
346
 
@@ -358,6 +395,7 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
358
  $order = parent::get_order_for_capture( $order, $amount );
359
 
360
  $order->capture->location_id = $this->get_order_meta( $order, 'square_location_id' );
 
361
 
362
  return $order;
363
  }
@@ -376,13 +414,16 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
376
  */
377
  protected function get_order_for_refund( $order, $amount, $reason ) {
378
 
379
- $order = parent::get_order_for_refund( $order, $amount, $reason );
 
380
 
381
  if ( $transaction_date = $this->get_order_meta( $order, 'trans_date' ) ) {
 
 
382
 
383
- // throw an error if the payment is > 120 days old
384
- if ( current_time( 'timestamp' ) - strtotime( $transaction_date ) > 120 * DAY_IN_SECONDS ) {
385
- return new \WP_Error( 'wc_square_refund_age_exceeded', __( 'Refunds must be made within 120 days of the original payment date.', 'woocommerce-square' ) );
386
  }
387
  }
388
 
@@ -392,8 +433,7 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
392
  if ( ! $order->refund->tender_id ) {
393
 
394
  try {
395
-
396
- $response = $this->get_api()->get_transaction( $order->refund->trans_id, $order->refund->location_id );
397
 
398
  if ( ! $response->get_authorization_code() ) {
399
  throw new Framework\SV_WC_Plugin_Exception( 'Tender missing' );
@@ -483,7 +523,7 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
483
  $order = parent::get_order_for_add_payment_method();
484
 
485
  // if the customer doesn't have a postcode yet, use the value returned by Square JS
486
- if ( ! $order->get_billing_postcode() && $postcode = Framework\SV_WC_Helper::get_post( 'wc-square-credit-card-payment-postcode') ) {
487
  $order->set_billing_postcode( $postcode );
488
  }
489
 
@@ -561,7 +601,7 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
561
 
562
  foreach ( $form_fields['card_types']['default'] as $key => $type ) {
563
 
564
- if ( in_array( $type, [ 'DISC', 'DINERS' ], true ) ) {
565
  unset( $form_fields['card_types']['default'][ $key ] );
566
  }
567
  }
@@ -634,7 +674,11 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
634
  * @param bool $enabled
635
  * @param Gateway $gateway_instance
636
  */
637
- return apply_filters( 'wc_square_is_3d_secure_enabled', true, $this );
 
 
 
 
638
  }
639
 
640
 
@@ -722,7 +766,7 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
722
  */
723
  protected function get_method_form_fields() {
724
 
725
- return [];
726
  }
727
 
728
 
@@ -737,7 +781,7 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
737
  * @param array $args arguments
738
  * @return string
739
  */
740
- public function get_customer_id( $user_id, $args = [] ) {
741
 
742
  // Square generates customer IDs
743
  $args['autocreate'] = false;
@@ -806,5 +850,50 @@ class Gateway extends Framework\SV_WC_Payment_Gateway_Direct {
806
  return apply_filters( 'wc_square_application_id', $square_application_id );
807
  }
808
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
 
810
  }
23
 
24
  namespace WooCommerce\Square;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model\Customer;
46
  /** @var Gateway\API API base instance */
47
  private $api;
48
 
49
+ /**
50
+ * As per documentation, as of now, SCA is enabled only for UK merchants, but to be implemented for Europe.
51
+ * As other currencies get supported, add them here.
52
+ *
53
+ * @since 2.2.0
54
+ *
55
+ * @var array $sca_supported_currencies Currencies for which SCA(3DS) is supported
56
+ */
57
+ private $sca_supported_currencies = array( 'GBP' );
58
 
59
  /**
60
  * Constructs the class.
63
  */
64
  public function __construct() {
65
 
66
+ parent::__construct(
67
+ Plugin::GATEWAY_ID,
68
+ wc_square(),
69
+ array(
70
+ 'method_title' => __( 'Square', 'woocommerce-square' ),
71
+ 'method_description' => __( 'Allow customers to use Square to securely pay with their credit cards', 'woocommerce-square' ),
72
+ 'payment_type' => self::PAYMENT_TYPE_CREDIT_CARD,
73
+ 'supports' => array(
74
+ self::FEATURE_PRODUCTS,
75
+ self::FEATURE_CARD_TYPES,
76
+ self::FEATURE_DETAILED_CUSTOMER_DECLINE_MESSAGES,
77
+ self::FEATURE_PAYMENT_FORM,
78
+ self::FEATURE_CREDIT_CARD_AUTHORIZATION,
79
+ self::FEATURE_CREDIT_CARD_CHARGE,
80
+ self::FEATURE_CREDIT_CARD_CHARGE_VIRTUAL,
81
+ self::FEATURE_CREDIT_CARD_CAPTURE,
82
+ self::FEATURE_REFUNDS,
83
+ self::FEATURE_VOIDS,
84
+ self::FEATURE_CUSTOMER_ID,
85
+ self::FEATURE_TOKENIZATION,
86
+ self::FEATURE_ADD_PAYMENT_METHOD,
87
+ self::FEATURE_TOKEN_EDITOR,
88
+ ),
89
+ )
90
+ );
91
 
92
  $this->view_transaction_url = 'https://squareup.com/dashboard/sales/transactions/%s';
93
 
94
  // log accept.js requests and responses
95
+ add_action( 'wp_ajax_wc_' . $this->get_id() . '_log_js_data', array( $this, 'log_js_data' ) );
96
+ add_action( 'wp_ajax_nopriv_wc_' . $this->get_id() . '_log_js_data', array( $this, 'log_js_data' ) );
97
 
98
  // store the Square item variation ID to order items
99
+ add_action( 'woocommerce_new_order_item', array( $this, 'store_new_order_item_square_meta' ), 10, 3 );
100
 
101
  // restore refunded Square inventory
102
+ add_action( 'woocommerce_order_fully_refunded', array( $this, 'restore_refunded_inventory' ), 10, 2 );
103
+
104
+ // AJAX Checkout validation handler.
105
+ add_action( 'wc_ajax_' . $this->get_id() . '_checkout_handler', array( $this, 'wc_ajax_square_checkout_handler' ) );
106
  }
107
 
108
 
160
  $item->save_meta_data();
161
  }
162
 
163
+ /**
164
+ * Overrides enqueue of the gateway-specific assets if present, including JS, CSS, and
165
+ * localized script params
166
+ *
167
+ * @since 2.1.7
168
+ */
169
+ protected function enqueue_payment_form_assets() {
170
+ // bail if *not* on add payment method page or checkout page.
171
+ if ( ! ( is_add_payment_method_page() || is_checkout() ) ) {
172
+ return;
173
+ }
174
+
175
+ parent::enqueue_payment_form_assets();
176
+ }
177
 
178
  /**
179
  * Enqueues the gateway JS.
181
  * @since 2.0.0
182
  */
183
  protected function enqueue_gateway_assets() {
184
+ // bail if *not* on add payment method page or checkout page.
185
+ if ( ! ( is_add_payment_method_page() || is_checkout() ) ) {
186
+ return;
187
+ }
188
+
189
  if ( $this->get_plugin()->get_settings_handler()->is_sandbox() ) {
190
  $url = 'https://js.squareupsandbox.com/v2/paymentform';
191
  } else {
192
  $url = 'https://js.squareup.com/v2/paymentform';
193
  }
194
 
195
+ wp_enqueue_script( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-payment-form', $url, array(), Plugin::VERSION );
196
 
197
  parent::enqueue_gateway_assets();
198
  }
222
  if ( ! Framework\SV_WC_Helper::get_post( 'wc-' . $this->get_id_dasherized() . '-payment-nonce' ) ) {
223
  throw new Framework\SV_WC_Payment_Gateway_Exception( 'Payment nonce is missing' );
224
  }
 
225
  } catch ( Framework\SV_WC_Payment_Gateway_Exception $exception ) {
226
 
227
  $is_valid = false;
261
  $order->payment->exp_year = Framework\SV_WC_Helper::get_post( 'wc-' . $this->get_id_dasherized() . '-exp-year' );
262
  }
263
 
 
264
  $order->square_customer_id = $order->customer_id;
265
+ $order->square_order_id = $this->get_order_meta( $order, 'square_order_id' );
266
+ $order->square_version = $this->get_order_meta( $order, 'square_version' );
267
 
268
  // look up in the index for guest customers
269
  if ( ! $order->get_user_id() ) {
289
  if ( ! $order->get_user_id() ) {
290
  Gateway\Customer_Helper::add_customer( $order->square_customer_id, $order->get_billing_email() );
291
  }
 
292
  } catch ( \Exception $exception ) {
293
 
294
  // log the error, but continue with payment
375
  if ( $response->get_square_order_id() ) {
376
  $this->update_order_meta( $order, 'square_order_id', $response->get_square_order_id() );
377
  }
378
+
379
+ // store the plugin version on the order
380
+ $this->update_order_meta( $order, 'square_version', Plugin::VERSION );
381
  }
382
 
383
 
395
  $order = parent::get_order_for_capture( $order, $amount );
396
 
397
  $order->capture->location_id = $this->get_order_meta( $order, 'square_location_id' );
398
+ $order->square_version = $this->get_order_meta( $order, 'square_version' );
399
 
400
  return $order;
401
  }
414
  */
415
  protected function get_order_for_refund( $order, $amount, $reason ) {
416
 
417
+ $order = parent::get_order_for_refund( $order, $amount, $reason );
418
+ $order->square_version = $this->get_order_meta( $order, 'square_version' );
419
 
420
  if ( $transaction_date = $this->get_order_meta( $order, 'trans_date' ) ) {
421
+ // refunds with the Refunds API can be made up to 1 year after payment and up to 120 days with the Transactions API
422
+ $max_refund_time = version_compare( $order->square_version, '2.2', '>=' ) ? '+1 year' : '+120 days';
423
 
424
+ // throw an error if the payment cannot be refunded
425
+ if ( current_time( 'timestamp' ) >= strtotime( $max_refund_time, strtotime( $transaction_date ) ) ) {
426
+ return new \WP_Error( 'wc_square_refund_age_exceeded', sprintf( __( 'Refunds must be made within %s of the original payment date.', 'woocommerce-square' ), '+1 year' === $max_refund_time ? 'a year' : '120 days' ) );
427
  }
428
  }
429
 
433
  if ( ! $order->refund->tender_id ) {
434
 
435
  try {
436
+ $response = version_compare( $order->square_version, '2.2', '>=' ) ? $this->get_api()->get_payment( $order->refund->trans_id ) : $this->get_api()->get_transaction( $order->refund->trans_id, $order->refund->location_id );
 
437
 
438
  if ( ! $response->get_authorization_code() ) {
439
  throw new Framework\SV_WC_Plugin_Exception( 'Tender missing' );
523
  $order = parent::get_order_for_add_payment_method();
524
 
525
  // if the customer doesn't have a postcode yet, use the value returned by Square JS
526
+ if ( ! $order->get_billing_postcode() && $postcode = Framework\SV_WC_Helper::get_post( 'wc-square-credit-card-payment-postcode' ) ) {
527
  $order->set_billing_postcode( $postcode );
528
  }
529
 
601
 
602
  foreach ( $form_fields['card_types']['default'] as $key => $type ) {
603
 
604
+ if ( in_array( $type, array( 'DISC', 'DINERS' ), true ) ) {
605
  unset( $form_fields['card_types']['default'][ $key ] );
606
  }
607
  }
674
  * @param bool $enabled
675
  * @param Gateway $gateway_instance
676
  */
677
+
678
+ $base_currency = get_woocommerce_currency();
679
+
680
+ $sca_enabled_currencies = in_array( $base_currency, $this->sca_supported_currencies, true );
681
+ return apply_filters( 'wc_square_is_3d_secure_enabled', $sca_enabled_currencies, $this );
682
  }
683
 
684
 
766
  */
767
  protected function get_method_form_fields() {
768
 
769
+ return array();
770
  }
771
 
772
 
781
  * @param array $args arguments
782
  * @return string
783
  */
784
+ public function get_customer_id( $user_id, $args = array() ) {
785
 
786
  // Square generates customer IDs
787
  $args['autocreate'] = false;
850
  return apply_filters( 'wc_square_application_id', $square_application_id );
851
  }
852
 
853
+ /**
854
+ * AJAX WooCommerce checkout validation handler
855
+ *
856
+ * Tap into woocommerce_after_checkout_validation hook
857
+ * and return WooCommerce checkout validation errors
858
+ *
859
+ * @since 2.2
860
+ */
861
+ public function wc_ajax_square_checkout_handler() {
862
+ // Nonce verfication.
863
+ if ( ! check_ajax_referer( 'wc_' . $this->get_id() . '_checkout_validate', 'wc_' . $this->get_id() . '_checkout_validate_nonce', false ) ) {
864
+ return wp_send_json_error( __( ' An error occurred, please try again or try an alternate form of payment.', 'woocommerce-square' ) );
865
+ }
866
+
867
+ // Nonce successfully verified. Proceed with validation.
868
+ add_action( 'woocommerce_after_checkout_validation', array( $this, 'wc_ajax_square_checkout_validate' ), 10, 2 );
869
+ WC()->checkout->process_checkout();
870
+ }
871
+
872
+ /**
873
+ * Validate WooCommerce checkout data on Square JS AJAX call
874
+ *
875
+ * Returns validation errors (or success) as JSON and exits to prevent checkout
876
+ *
877
+ * @since 2.2
878
+ *
879
+ * @param array $data WooCommerce checkout POST data.
880
+ * @param WP_Error $errors WooCommerce checkout errors.
881
+ */
882
+ public function wc_ajax_square_checkout_validate( $data, $errors = null ) {
883
+ $error_messages = null;
884
+ if ( ! is_null( $errors ) ) {
885
+ $error_messages = $errors->get_error_messages();
886
+ }
887
+
888
+ // Clear all existing notices.
889
+ wc_clear_notices();
890
+
891
+ if ( empty( $error_messages ) ) {
892
+ wp_send_json_success( 'validation_successful' );
893
+ } else {
894
+ wp_send_json_error( array( 'messages' => $error_messages ) );
895
+ }
896
+ exit;
897
+ }
898
 
899
  }
includes/Gateway/API.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model\Order;
@@ -73,11 +73,11 @@ class API extends \WooCommerce\Square\API {
73
  */
74
  public function credit_card_authorization( \WC_Order $order ) {
75
 
76
- $request = new API\Requests\Transactions( $this->get_location_id(), $this->client );
77
 
78
  $request->set_authorization_data( $order );
79
 
80
- $this->set_response_handler( API\Responses\Charge::class );
81
 
82
  return $this->perform_request( $request );
83
  }
@@ -94,11 +94,11 @@ class API extends \WooCommerce\Square\API {
94
  */
95
  public function credit_card_charge( \WC_Order $order ) {
96
 
97
- $request = new API\Requests\Transactions( $this->get_location_id(), $this->client );
98
 
99
  $request->set_charge_data( $order );
100
 
101
- $this->set_response_handler( API\Responses\Charge::class );
102
 
103
  return $this->perform_request( $request );
104
  }
@@ -117,7 +117,12 @@ class API extends \WooCommerce\Square\API {
117
 
118
  $location_id = ! empty( $order->capture->location_id ) ? $order->capture->location_id : $this->get_location_id();
119
 
120
- $request = new API\Requests\Transactions( $location_id, $this->client );
 
 
 
 
 
121
 
122
  $request->set_capture_data( $order );
123
 
@@ -140,7 +145,12 @@ class API extends \WooCommerce\Square\API {
140
 
141
  $location_id = ! empty( $order->refund->location_id ) ? $order->refund->location_id : $this->get_location_id();
142
 
143
- $request = new API\Requests\Transactions( $location_id, $this->client );
 
 
 
 
 
144
 
145
  $request->set_refund_data( $order );
146
 
@@ -163,7 +173,12 @@ class API extends \WooCommerce\Square\API {
163
 
164
  $location_id = ! empty( $order->refund->location_id ) ? $order->refund->location_id : $this->get_location_id();
165
 
166
- $request = new API\Requests\Transactions( $location_id, $this->client );
 
 
 
 
 
167
 
168
  $request->set_void_data( $order );
169
 
@@ -376,6 +391,27 @@ class API extends \WooCommerce\Square\API {
376
  }
377
 
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  /**
380
  * Validates the parsed response.
381
  *
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model\Order;
73
  */
74
  public function credit_card_authorization( \WC_Order $order ) {
75
 
76
+ $request = new API\Requests\Payments( $this->get_location_id(), $this->client );
77
 
78
  $request->set_authorization_data( $order );
79
 
80
+ $this->set_response_handler( API\Responses\Create_Payment::class );
81
 
82
  return $this->perform_request( $request );
83
  }
94
  */
95
  public function credit_card_charge( \WC_Order $order ) {
96
 
97
+ $request = new API\Requests\Payments( $this->get_location_id(), $this->client );
98
 
99
  $request->set_charge_data( $order );
100
 
101
+ $this->set_response_handler( API\Responses\Create_Payment::class );
102
 
103
  return $this->perform_request( $request );
104
  }
117
 
118
  $location_id = ! empty( $order->capture->location_id ) ? $order->capture->location_id : $this->get_location_id();
119
 
120
+ // use the Payments API to capture orders that were processed with Square v2.2+
121
+ if ( ! empty( $order->square_version ) && version_compare( $order->square_version, '2.2', '>=' ) ) {
122
+ $request = new API\Requests\Payments( $location_id, $this->client );
123
+ } else {
124
+ $request = new API\Requests\Transactions( $location_id, $this->client );
125
+ }
126
 
127
  $request->set_capture_data( $order );
128
 
145
 
146
  $location_id = ! empty( $order->refund->location_id ) ? $order->refund->location_id : $this->get_location_id();
147
 
148
+ // only use the Refunds API to refund orders that took payment after Square v2.2
149
+ if ( ! empty( $order->square_version ) && version_compare( $order->square_version, '2.2', '>=' ) ) {
150
+ $request = new API\Requests\Refunds( $this->client );
151
+ } else {
152
+ $request = new API\Requests\Transactions( $location_id, $this->client );
153
+ }
154
 
155
  $request->set_refund_data( $order );
156
 
173
 
174
  $location_id = ! empty( $order->refund->location_id ) ? $order->refund->location_id : $this->get_location_id();
175
 
176
+ // use the Payments API to void/cancel orders that were processed after Square v2.2
177
+ if ( ! empty( $order->square_version ) && version_compare( $order->square_version, '2.2', '>=' ) ) {
178
+ $request = new API\Requests\Payments( $location_id, $this->client );
179
+ } else {
180
+ $request = new API\Requests\Transactions( $location_id, $this->client );
181
+ }
182
 
183
  $request->set_void_data( $order );
184
 
391
  }
392
 
393
 
394
+ /**
395
+ * Gets an existing payment.
396
+ *
397
+ * @since 2.2.0
398
+ *
399
+ * @param string $payment_id transaction ID
400
+ * @return API\Responses\Create_Payment
401
+ * @throws Framework\SV_WC_API_Exception
402
+ */
403
+ public function get_payment( $payment_id ) {
404
+
405
+ $request = new API\Requests\Payments( $this->get_location_id(), $this->client );
406
+
407
+ $request->set_get_payment_data( $payment_id );
408
+
409
+ $this->set_response_handler( API\Responses\Create_Payment::class );
410
+
411
+ return $this->perform_request( $request );
412
+ }
413
+
414
+
415
  /**
416
  * Validates the parsed response.
417
  *
includes/Gateway/API/Requests/Customers.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Requests;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model as SquareModel;
@@ -59,8 +59,7 @@ class Customers extends API\Requests\Customers {
59
 
60
  $email = $customer->get_email();
61
 
62
- // otherwise, use the order billing email
63
- } catch ( \Exception $exception ) {
64
 
65
  $email = $order->get_billing_email();
66
  }
@@ -80,9 +79,9 @@ class Customers extends API\Requests\Customers {
80
 
81
  $this->square_request = $customer_request;
82
 
83
- $this->square_api_args = [
84
  $this->square_request,
85
- ];
86
  }
87
 
88
 
@@ -110,10 +109,10 @@ class Customers extends API\Requests\Customers {
110
 
111
  $this->square_request = $request;
112
 
113
- $this->square_api_args = [
114
  $order->customer_id,
115
  $this->square_request,
116
- ];
117
  }
118
 
119
 
@@ -129,10 +128,10 @@ class Customers extends API\Requests\Customers {
129
 
130
  $this->square_api_method = 'deleteCustomerCard';
131
 
132
- $this->square_api_args = [
133
  $customer_id,
134
  $card_id,
135
- ];
136
  }
137
 
138
 
23
 
24
  namespace WooCommerce\Square\Gateway\API\Requests;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model as SquareModel;
59
 
60
  $email = $customer->get_email();
61
 
62
+ } catch ( \Exception $exception ) { // otherwise, use the order billing email
 
63
 
64
  $email = $order->get_billing_email();
65
  }
79
 
80
  $this->square_request = $customer_request;
81
 
82
+ $this->square_api_args = array(
83
  $this->square_request,
84
+ );
85
  }
86
 
87
 
109
 
110
  $this->square_request = $request;
111
 
112
+ $this->square_api_args = array(
113
  $order->customer_id,
114
  $this->square_request,
115
+ );
116
  }
117
 
118
 
128
 
129
  $this->square_api_method = 'deleteCustomerCard';
130
 
131
+ $this->square_api_args = array(
132
  $customer_id,
133
  $card_id,
134
+ );
135
  }
136
 
137
 
includes/Gateway/API/Requests/Orders.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Requests;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Api\OrdersApi;
@@ -78,21 +78,27 @@ class Orders extends API\Request {
78
 
79
  if ( $order->get_discount_total() ) {
80
 
81
- $order_model->setDiscounts( [ new SquareModel\OrderLineItemDiscount( [
82
- 'name' => __( 'Discount', 'woocommerce-square' ),
83
- 'type' => 'FIXED_AMOUNT',
84
- 'amount_money' => Money_Utility::amount_to_money( $order->get_discount_total(), $order->get_currency() ),
85
- 'scope' => 'ORDER',
86
- ] ) ] );
 
 
 
 
 
 
87
  }
88
 
89
  $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) );
90
  $this->square_request->setOrder( $order_model );
91
 
92
- $this->square_api_args = [
93
  $location_id,
94
  $this->square_request,
95
- ];
96
  }
97
 
98
 
@@ -106,7 +112,7 @@ class Orders extends API\Request {
106
  */
107
  protected function get_product_line_items( \WC_Order $order ) {
108
 
109
- $line_items = [];
110
 
111
  foreach ( $order->get_items() as $item ) {
112
 
@@ -144,7 +150,7 @@ class Orders extends API\Request {
144
  */
145
  protected function get_fee_line_items( \WC_Order $order ) {
146
 
147
- $line_items = [];
148
 
149
  foreach ( $order->get_fees() as $item ) {
150
 
@@ -176,7 +182,7 @@ class Orders extends API\Request {
176
  */
177
  protected function get_shipping_line_items( \WC_Order $order ) {
178
 
179
- $line_items = [];
180
 
181
  foreach ( $order->get_shipping_methods() as $item ) {
182
 
@@ -208,16 +214,18 @@ class Orders extends API\Request {
208
  */
209
  protected function get_order_taxes( \WC_Order $order ) {
210
 
211
- $taxes = [];
212
 
213
  foreach ( $order->get_taxes() as $tax ) {
214
 
215
- $tax_item = new SquareModel\OrderLineItemTax( [
216
- 'uid' => uniqid(),
217
- 'name' => $tax->get_name(),
218
- 'type' => 'ADDITIVE',
219
- 'scope' => 'LINE_ITEM',
220
- ] );
 
 
221
 
222
  $pre_tax_total = (float) $order->get_total() - (float) $order->get_total_tax();
223
  $total_tax = (float) $tax->get_tax_total() + (float) $tax->get_shipping_tax_total();
@@ -245,13 +253,15 @@ class Orders extends API\Request {
245
 
246
  foreach ( $line_items as $line_item ) {
247
 
248
- $applied_taxes = [];
249
 
250
  foreach ( $taxes as $tax ) {
251
 
252
- $applied_taxes[] = new SquareModel\OrderLineItemAppliedTax( [
253
- 'tax_uid' => $tax->getUid(),
254
- ] );
 
 
255
  }
256
 
257
  $line_item->setAppliedTaxes( $applied_taxes );
@@ -277,23 +287,31 @@ class Orders extends API\Request {
277
  $order_model = new SquareModel\Order();
278
  $order_model->setVersion( $version );
279
 
280
- $order_model->setLineItems( [ new SquareModel\OrderLineItem( [
281
- 'name' => __( 'Adjustment', 'woocommerce-square' ),
282
- 'quantity' => (string) 1,
283
- 'base_price_money' => new SquareModel\Money( [
284
- 'amount' => $amount,
285
- 'currency' => $order->get_currency(),
286
- ] ),
287
- ] ) ] );
 
 
 
 
 
 
 
 
288
 
289
  $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) . $version );
290
  $this->square_request->setOrder( $order_model );
291
 
292
- $this->square_api_args = [
293
  $location_id,
294
  $order->square_order_id,
295
  $this->square_request,
296
- ];
297
  }
298
 
299
 
@@ -315,24 +333,32 @@ class Orders extends API\Request {
315
  $order_model = new SquareModel\Order();
316
  $order_model->setVersion( $version );
317
 
318
- $order_model->setDiscounts( [ new SquareModel\OrderLineItemDiscount( [
319
- 'name' => __( 'Adjustment', 'woocommerce-square' ),
320
- 'type' => 'FIXED_AMOUNT',
321
- 'amount_money' => new SquareModel\Money( [
322
- 'amount' => $amount,
323
- 'currency' => $order->get_currency(),
324
- ] ),
325
- 'scope' => 'ORDER',
326
- ] ) ] );
 
 
 
 
 
 
 
 
327
 
328
  $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) . $version );
329
  $this->square_request->setOrder( $order_model );
330
 
331
- $this->square_api_args = [
332
  $location_id,
333
  $order->square_order_id,
334
  $this->square_request,
335
- ];
336
  }
337
 
338
  }
23
 
24
  namespace WooCommerce\Square\Gateway\API\Requests;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Api\OrdersApi;
78
 
79
  if ( $order->get_discount_total() ) {
80
 
81
+ $order_model->setDiscounts(
82
+ array(
83
+ new SquareModel\OrderLineItemDiscount(
84
+ array(
85
+ 'name' => __( 'Discount', 'woocommerce-square' ),
86
+ 'type' => 'FIXED_AMOUNT',
87
+ 'amount_money' => Money_Utility::amount_to_money( $order->get_discount_total(), $order->get_currency() ),
88
+ 'scope' => 'ORDER',
89
+ )
90
+ ),
91
+ )
92
+ );
93
  }
94
 
95
  $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) );
96
  $this->square_request->setOrder( $order_model );
97
 
98
+ $this->square_api_args = array(
99
  $location_id,
100
  $this->square_request,
101
+ );
102
  }
103
 
104
 
112
  */
113
  protected function get_product_line_items( \WC_Order $order ) {
114
 
115
+ $line_items = array();
116
 
117
  foreach ( $order->get_items() as $item ) {
118
 
150
  */
151
  protected function get_fee_line_items( \WC_Order $order ) {
152
 
153
+ $line_items = array();
154
 
155
  foreach ( $order->get_fees() as $item ) {
156
 
182
  */
183
  protected function get_shipping_line_items( \WC_Order $order ) {
184
 
185
+ $line_items = array();
186
 
187
  foreach ( $order->get_shipping_methods() as $item ) {
188
 
214
  */
215
  protected function get_order_taxes( \WC_Order $order ) {
216
 
217
+ $taxes = array();
218
 
219
  foreach ( $order->get_taxes() as $tax ) {
220
 
221
+ $tax_item = new SquareModel\OrderLineItemTax(
222
+ array(
223
+ 'uid' => uniqid(),
224
+ 'name' => $tax->get_name(),
225
+ 'type' => 'ADDITIVE',
226
+ 'scope' => 'LINE_ITEM',
227
+ )
228
+ );
229
 
230
  $pre_tax_total = (float) $order->get_total() - (float) $order->get_total_tax();
231
  $total_tax = (float) $tax->get_tax_total() + (float) $tax->get_shipping_tax_total();
253
 
254
  foreach ( $line_items as $line_item ) {
255
 
256
+ $applied_taxes = array();
257
 
258
  foreach ( $taxes as $tax ) {
259
 
260
+ $applied_taxes[] = new SquareModel\OrderLineItemAppliedTax(
261
+ array(
262
+ 'tax_uid' => $tax->getUid(),
263
+ )
264
+ );
265
  }
266
 
267
  $line_item->setAppliedTaxes( $applied_taxes );
287
  $order_model = new SquareModel\Order();
288
  $order_model->setVersion( $version );
289
 
290
+ $order_model->setLineItems(
291
+ array(
292
+ new SquareModel\OrderLineItem(
293
+ array(
294
+ 'name' => __( 'Adjustment', 'woocommerce-square' ),
295
+ 'quantity' => (string) 1,
296
+ 'base_price_money' => new SquareModel\Money(
297
+ array(
298
+ 'amount' => $amount,
299
+ 'currency' => $order->get_currency(),
300
+ )
301
+ ),
302
+ )
303
+ ),
304
+ )
305
+ );
306
 
307
  $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) . $version );
308
  $this->square_request->setOrder( $order_model );
309
 
310
+ $this->square_api_args = array(
311
  $location_id,
312
  $order->square_order_id,
313
  $this->square_request,
314
+ );
315
  }
316
 
317
 
333
  $order_model = new SquareModel\Order();
334
  $order_model->setVersion( $version );
335
 
336
+ $order_model->setDiscounts(
337
+ array(
338
+ new SquareModel\OrderLineItemDiscount(
339
+ array(
340
+ 'name' => __( 'Adjustment', 'woocommerce-square' ),
341
+ 'type' => 'FIXED_AMOUNT',
342
+ 'amount_money' => new SquareModel\Money(
343
+ array(
344
+ 'amount' => $amount,
345
+ 'currency' => $order->get_currency(),
346
+ )
347
+ ),
348
+ 'scope' => 'ORDER',
349
+ )
350
+ ),
351
+ )
352
+ );
353
 
354
  $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) . $version );
355
  $this->square_request->setOrder( $order_model );
356
 
357
+ $this->square_api_args = array(
358
  $location_id,
359
  $order->square_order_id,
360
  $this->square_request,
361
+ );
362
  }
363
 
364
  }
includes/Gateway/API/Requests/Payments.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WooCommerce Square
4
+ *
5
+ * This source file is subject to the GNU General Public License v3.0
6
+ * that is bundled with this package in the file license.txt.
7
+ * It is also available through the world-wide-web at this URL:
8
+ * http://www.gnu.org/licenses/gpl-3.0.html
9
+ * If you did not receive a copy of the license and are unable to
10
+ * obtain it through the world-wide-web, please send an email
11
+ * to license@woocommerce.com so we can send you a copy immediately.
12
+ *
13
+ * DISCLAIMER
14
+ *
15
+ * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16
+ * versions in the future. If you wish to customize WooCommerce Square for your
17
+ * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18
+ *
19
+ * @author WooCommerce
20
+ * @copyright Copyright: (c) 2019, Automattic, Inc.
21
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
+ */
23
+
24
+ namespace WooCommerce\Square\Gateway\API\Requests;
25
+
26
+ defined( 'ABSPATH' ) || exit;
27
+
28
+ use SquareConnect\Api\PaymentsApi;
29
+ use SquareConnect\Model as SquareModel;
30
+ use WooCommerce\Square\Utilities;
31
+
32
+ /**
33
+ * The Payments API request class.
34
+ *
35
+ * @since 2.2.0
36
+ */
37
+ class Payments extends \WooCommerce\Square\API\Request {
38
+
39
+ /** @var string location ID */
40
+ protected $location_id;
41
+
42
+ /**
43
+ * Initializes a new payments request.
44
+ *
45
+ * @since 2.2.0
46
+ * @param string $location_id location ID
47
+ * @param \SquareConnect\ApiClient $api_client the API client
48
+ */
49
+ public function __construct( $location_id, $api_client ) {
50
+ $this->location_id = $location_id;
51
+ $this->square_api = new PaymentsApi( $api_client );
52
+ }
53
+
54
+ /**
55
+ * Sets the data for an authorization/delayed capture.
56
+ *
57
+ * @since 2.2.0
58
+ * @param \WC_Order $order order object
59
+ */
60
+ public function set_authorization_data( \WC_Order $order ) {
61
+ $this->set_charge_data( $order, false );
62
+ }
63
+
64
+ /**
65
+ * Sets the data for a charge.
66
+ *
67
+ * @since 2.2.0
68
+ *
69
+ * @param \WC_Order $order
70
+ * @param bool $capture whether to immediately capture the charge
71
+ */
72
+ public function set_charge_data( \WC_Order $order, $capture = true ) {
73
+
74
+ $this->square_api_method = 'createPayment';
75
+ $this->square_request = new SquareModel\CreatePaymentRequest();
76
+
77
+ $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->unique_transaction_ref ) );
78
+ $this->square_request->setAmountMoney( Utilities\Money_Utility::amount_to_money( $order->payment_total, $order->get_currency() ) );
79
+ $this->square_request->setReferenceId( $order->get_order_number() );
80
+
81
+ /**
82
+ * Filters the Square payment order note (legacy filter).
83
+ *
84
+ * @since 2.2.0
85
+ *
86
+ * @param string $description the order note (description)
87
+ * @param \WC_Order $order the order object
88
+ */
89
+ $description = (string) apply_filters( 'wc_square_payment_order_note', $order->description, $order );
90
+
91
+ $this->square_request->setNote( Utilities\String_Utility::truncate( $description, 500 ) );
92
+
93
+ $this->square_request->setAutocomplete( $capture );
94
+
95
+ if ( ! empty( $order->square_customer_id ) ) {
96
+ $this->square_request->setCustomerId( $order->square_customer_id );
97
+ }
98
+
99
+ // payment token (card ID) or card nonce (from JS)
100
+ $this->square_request->setSourceId( ! empty( $order->payment->token ) ? $order->payment->token : $order->payment->nonce );
101
+
102
+ // 3DS / SCA verification token (from JS)
103
+ if ( ! empty( $order->payment->verification_token ) ) {
104
+ $this->square_request->setVerificationToken( $order->payment->verification_token );
105
+ }
106
+
107
+ if ( ! empty( $this->location_id ) ) {
108
+ $this->square_request->setLocationId( $this->location_id );
109
+ }
110
+
111
+ $billing_address = new SquareModel\Address();
112
+ $billing_address->setFirstName( $order->get_billing_first_name() );
113
+ $billing_address->setLastName( $order->get_billing_last_name() );
114
+ $billing_address->setOrganization( $order->get_billing_company() );
115
+ $billing_address->setAddressLine1( $order->get_billing_address_1() );
116
+ $billing_address->setAddressLine2( $order->get_billing_address_2() );
117
+ $billing_address->setLocality( $order->get_billing_city() );
118
+ $billing_address->setAdministrativeDistrictLevel1( $order->get_billing_state() );
119
+ $billing_address->setPostalCode( $order->get_billing_postcode() );
120
+ $billing_address->setCountry( $order->get_billing_country() );
121
+
122
+ $this->square_request->setBillingAddress( $billing_address );
123
+
124
+ if ( $order->get_shipping_address_1( 'edit' ) || $order->get_shipping_address_2( 'edit' ) ) {
125
+
126
+ $shipping_address = new SquareModel\Address();
127
+ $shipping_address->setFirstName( $order->get_shipping_first_name() );
128
+ $shipping_address->setLastName( $order->get_shipping_last_name() );
129
+ $shipping_address->setAddressLine1( $order->get_shipping_address_1() );
130
+ $shipping_address->setAddressLine2( $order->get_shipping_address_2() );
131
+ $shipping_address->setLocality( $order->get_shipping_city() );
132
+ $shipping_address->setAdministrativeDistrictLevel1( $order->get_shipping_state() );
133
+ $shipping_address->setPostalCode( $order->get_shipping_postcode() );
134
+ $shipping_address->setCountry( $order->get_shipping_country() );
135
+
136
+ $this->square_request->setShippingAddress( $shipping_address );
137
+ }
138
+
139
+ $this->square_request->setBuyerEmailAddress( $order->get_billing_email() );
140
+
141
+ if ( ! empty( $order->square_order_id ) ) {
142
+ $this->square_request->setOrderId( $order->square_order_id );
143
+ }
144
+
145
+ $this->square_api_args = array(
146
+ $this->square_request,
147
+ );
148
+ }
149
+
150
+
151
+ /**
152
+ * Sets the data for capturing a payment.
153
+ *
154
+ * @since 2.2.0
155
+ *
156
+ * @param \WC_Order $order order object
157
+ */
158
+ public function set_capture_data( \WC_Order $order ) {
159
+ $this->square_api_method = 'completePayment';
160
+ $this->square_api_args = array( $order->capture->trans_id );
161
+ }
162
+
163
+
164
+ /**
165
+ * Sets the data for voiding/cancelling a payment.
166
+ *
167
+ * @since 2.2.0
168
+ *
169
+ * @param \WC_Order $order order object
170
+ */
171
+ public function set_void_data( \WC_Order $order ) {
172
+ $this->square_api_method = 'cancelPayment';
173
+ $this->square_api_args = array( $order->refund->trans_id );
174
+ }
175
+
176
+
177
+ /**
178
+ * Sets the data for getting a Payment.
179
+ *
180
+ * @since 2.2.0
181
+ *
182
+ * @param string $payment_id payment ID
183
+ */
184
+ public function set_get_payment_data( $payment_id ) {
185
+ $this->square_api_method = 'getPayment';
186
+ $this->square_api_args = array( $payment_id );
187
+ }
188
+
189
+
190
+ /** Getter methods ************************************************************************************************/
191
+
192
+
193
+ /** Gets the location ID for this request.
194
+ *
195
+ * All requests in this type must have a location ID.
196
+ *
197
+ * @since 2.0.0
198
+ *
199
+ * @return string
200
+ */
201
+ protected function get_location_id() {
202
+
203
+ return $this->location_id;
204
+ }
205
+ }
includes/Gateway/API/Requests/Refunds.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WooCommerce Square
4
+ *
5
+ * This source file is subject to the GNU General Public License v3.0
6
+ * that is bundled with this package in the file license.txt.
7
+ * It is also available through the world-wide-web at this URL:
8
+ * http://www.gnu.org/licenses/gpl-3.0.html
9
+ * If you did not receive a copy of the license and are unable to
10
+ * obtain it through the world-wide-web, please send an email
11
+ * to license@woocommerce.com so we can send you a copy immediately.
12
+ *
13
+ * DISCLAIMER
14
+ *
15
+ * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16
+ * versions in the future. If you wish to customize WooCommerce Square for your
17
+ * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18
+ *
19
+ * @author WooCommerce
20
+ * @copyright Copyright: (c) 2019, Automattic, Inc.
21
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
+ */
23
+
24
+ namespace WooCommerce\Square\Gateway\API\Requests;
25
+
26
+ defined( 'ABSPATH' ) || exit;
27
+
28
+ use SquareConnect\Api\RefundsApi;
29
+ use SquareConnect\Model\RefundPaymentRequest;
30
+ use WooCommerce\Square\Utilities\Money_Utility;
31
+
32
+ /**
33
+ * The Refunds API request class.
34
+ *
35
+ * @since 2.2.0
36
+ */
37
+ class Refunds extends \WooCommerce\Square\API\Request {
38
+
39
+ /**
40
+ * Initializes a new refund request.
41
+ *
42
+ * @since 2.2.0
43
+ * @param \SquareConnect\ApiClient $api_client the API client
44
+ */
45
+ public function __construct( $api_client ) {
46
+ $this->square_api = new RefundsApi( $api_client );
47
+ }
48
+
49
+
50
+ /**
51
+ * Sets the data for refund a payment.
52
+ *
53
+ * @since 2.2.0
54
+ *
55
+ * @param \WC_Order $order order object
56
+ */
57
+ public function set_refund_data( \WC_Order $order ) {
58
+
59
+ $this->square_api_method = 'refundPayment';
60
+
61
+ // The refund objects are sorted by date DESC, so the last one created will be at the start of the array
62
+ $refunds = $order->get_refunds();
63
+ $refund_obj = $refunds[0];
64
+
65
+ $this->square_request = new RefundPaymentRequest();
66
+ $this->square_request->setIdempotencyKey( wc_square()->get_idempotency_key( $order->get_id() . ':' . $refund_obj->get_id() ) );
67
+ $this->square_request->setPaymentId( $order->refund->tender_id );
68
+ $this->square_request->setReason( $order->refund->reason );
69
+
70
+ $this->square_request->setAmountMoney( Money_Utility::amount_to_money( $order->refund->amount, $order->get_currency() ) );
71
+
72
+ $this->square_api_args = array(
73
+ $this->square_request,
74
+ );
75
+ }
76
+ }
includes/Gateway/API/Requests/Transactions.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Requests;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Api\TransactionsApi;
@@ -148,10 +148,10 @@ class Transactions extends \WooCommerce\Square\API\Request {
148
  $this->square_request->setOrderId( $order->square_order_id );
149
  }
150
 
151
- $this->square_api_args = [
152
  $this->get_location_id(),
153
  $this->square_request,
154
- ];
155
  }
156
 
157
 
@@ -166,10 +166,10 @@ class Transactions extends \WooCommerce\Square\API\Request {
166
 
167
  $this->square_api_method = 'captureTransaction';
168
 
169
- $this->square_api_args = [
170
  $this->get_location_id(),
171
  $order->capture->trans_id,
172
- ];
173
  }
174
 
175
 
@@ -185,7 +185,7 @@ class Transactions extends \WooCommerce\Square\API\Request {
185
  $this->square_api_method = 'createRefund';
186
 
187
  // The refund objects are sorted by date DESC, so the last one created will be at the start of the array
188
- $refunds = $order->get_refunds();
189
  $refund_obj = $refunds[0];
190
 
191
  $this->square_request = new CreateRefundRequest();
@@ -195,11 +195,11 @@ class Transactions extends \WooCommerce\Square\API\Request {
195
 
196
  $this->square_request->setAmountMoney( Money_Utility::amount_to_money( $order->refund->amount, $order->get_currency() ) );
197
 
198
- $this->square_api_args = [
199
  $this->get_location_id(),
200
  $order->refund->trans_id,
201
  $this->square_request,
202
- ];
203
  }
204
 
205
 
@@ -214,10 +214,10 @@ class Transactions extends \WooCommerce\Square\API\Request {
214
 
215
  $this->square_api_method = 'voidTransaction';
216
 
217
- $this->square_api_args = [
218
  $this->get_location_id(),
219
  $order->refund->trans_id,
220
- ];
221
  }
222
 
223
 
@@ -232,10 +232,10 @@ class Transactions extends \WooCommerce\Square\API\Request {
232
 
233
  $this->square_api_method = 'retrieveTransaction';
234
 
235
- $this->square_api_args = [
236
  $this->get_location_id(),
237
  $transaction_id,
238
- ];
239
  }
240
 
241
 
23
 
24
  namespace WooCommerce\Square\Gateway\API\Requests;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Api\TransactionsApi;
148
  $this->square_request->setOrderId( $order->square_order_id );
149
  }
150
 
151
+ $this->square_api_args = array(
152
  $this->get_location_id(),
153
  $this->square_request,
154
+ );
155
  }
156
 
157
 
166
 
167
  $this->square_api_method = 'captureTransaction';
168
 
169
+ $this->square_api_args = array(
170
  $this->get_location_id(),
171
  $order->capture->trans_id,
172
+ );
173
  }
174
 
175
 
185
  $this->square_api_method = 'createRefund';
186
 
187
  // The refund objects are sorted by date DESC, so the last one created will be at the start of the array
188
+ $refunds = $order->get_refunds();
189
  $refund_obj = $refunds[0];
190
 
191
  $this->square_request = new CreateRefundRequest();
195
 
196
  $this->square_request->setAmountMoney( Money_Utility::amount_to_money( $order->refund->amount, $order->get_currency() ) );
197
 
198
+ $this->square_api_args = array(
199
  $this->get_location_id(),
200
  $order->refund->trans_id,
201
  $this->square_request,
202
+ );
203
  }
204
 
205
 
214
 
215
  $this->square_api_method = 'voidTransaction';
216
 
217
+ $this->square_api_args = array(
218
  $this->get_location_id(),
219
  $order->refund->trans_id,
220
+ );
221
  }
222
 
223
 
232
 
233
  $this->square_api_method = 'retrieveTransaction';
234
 
235
+ $this->square_api_args = array(
236
  $this->get_location_id(),
237
  $transaction_id,
238
+ );
239
  }
240
 
241
 
includes/Gateway/API/Response.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Gateway;
23
 
24
  namespace WooCommerce\Square\Gateway\API;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Gateway;
includes/Gateway/API/Responses/Charge.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -153,19 +153,19 @@ class Charge extends \WooCommerce\Square\Gateway\API\Response implements Framewo
153
 
154
  case 'CARD_DECLINED':
155
  $message_id = 'card_declined';
156
- break;
157
 
158
  case 'INVALID_EXPIRATION':
159
  $message_id = 'card_expiry_invalid';
160
- break;
161
 
162
  case 'VERIFY_AVS_FAILURE':
163
  $message_id = 'avs_mismatch';
164
- break;
165
 
166
  case 'VERIFY_CVV_FAILURE':
167
  $message_id = 'csc_mismatch';
168
- break;
169
  }
170
 
171
  $helper = new Framework\SV_WC_Payment_Gateway_API_Response_Message_Helper();
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
153
 
154
  case 'CARD_DECLINED':
155
  $message_id = 'card_declined';
156
+ break;
157
 
158
  case 'INVALID_EXPIRATION':
159
  $message_id = 'card_expiry_invalid';
160
+ break;
161
 
162
  case 'VERIFY_AVS_FAILURE':
163
  $message_id = 'avs_mismatch';
164
+ break;
165
 
166
  case 'VERIFY_CVV_FAILURE':
167
  $message_id = 'csc_mismatch';
168
+ break;
169
  }
170
 
171
  $helper = new Framework\SV_WC_Payment_Gateway_API_Response_Message_Helper();
includes/Gateway/API/Responses/Create_Customer.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
includes/Gateway/API/Responses/Create_Customer_Card.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -46,7 +46,7 @@ class Create_Customer_Card extends \WooCommerce\Square\Gateway\API\Response impl
46
  */
47
  public function get_payment_token() {
48
 
49
- $card = $this->get_data() instanceof \SquareConnect\Model\CreateCustomerCardResponse ? $this->get_data()->getCard() : null;
50
  $token = null;
51
 
52
  if ( $card ) {
@@ -55,13 +55,13 @@ class Create_Customer_Card extends \WooCommerce\Square\Gateway\API\Response impl
55
 
56
  $token = new Framework\SV_WC_Payment_Gateway_Payment_Token(
57
  $card->getId(),
58
- [
59
  'type' => 'credit_card',
60
  'card_type' => $card_type,
61
  'last_four' => $card->getLast4(),
62
  'exp_month' => $card->getExpMonth(),
63
  'exp_year' => $card->getExpYear(),
64
- ]
65
  );
66
  }
67
 
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
46
  */
47
  public function get_payment_token() {
48
 
49
+ $card = $this->get_data() instanceof \SquareConnect\Model\CreateCustomerCardResponse ? $this->get_data()->getCard() : null;
50
  $token = null;
51
 
52
  if ( $card ) {
55
 
56
  $token = new Framework\SV_WC_Payment_Gateway_Payment_Token(
57
  $card->getId(),
58
+ array(
59
  'type' => 'credit_card',
60
  'card_type' => $card_type,
61
  'last_four' => $card->getLast4(),
62
  'exp_month' => $card->getExpMonth(),
63
  'exp_year' => $card->getExpYear(),
64
+ )
65
  );
66
  }
67
 
includes/Gateway/API/Responses/Create_Payment.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WooCommerce Square
4
+ *
5
+ * This source file is subject to the GNU General Public License v3.0
6
+ * that is bundled with this package in the file license.txt.
7
+ * It is also available through the world-wide-web at this URL:
8
+ * http://www.gnu.org/licenses/gpl-3.0.html
9
+ * If you did not receive a copy of the license and are unable to
10
+ * obtain it through the world-wide-web, please send an email
11
+ * to license@woocommerce.com so we can send you a copy immediately.
12
+ *
13
+ * DISCLAIMER
14
+ *
15
+ * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16
+ * versions in the future. If you wish to customize WooCommerce Square for your
17
+ * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18
+ *
19
+ * @author WooCommerce
20
+ * @copyright Copyright: (c) 2019, Automattic, Inc.
21
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
+ */
23
+
24
+ namespace WooCommerce\Square\Gateway\API\Responses;
25
+
26
+ defined( 'ABSPATH' ) || exit;
27
+
28
+ use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
+
30
+ /**
31
+ * The Create Payment API response object.
32
+ *
33
+ * @since 2.2.0
34
+ *
35
+ * @method \SquareConnect\Model\CreatePaymentResponse get_data()
36
+ */
37
+ class Create_Payment extends \WooCommerce\Square\Gateway\API\Response implements Framework\SV_WC_Payment_Gateway_API_Authorization_Response {
38
+
39
+
40
+ /**
41
+ * Determines if the charge was held.
42
+ *
43
+ * @since 2.2.0
44
+ *
45
+ * @return bool
46
+ */
47
+ public function transaction_held() {
48
+
49
+ $held = parent::transaction_held();
50
+
51
+ // ensure the tender is CAPTURED
52
+ if ( $this->get_payment() ) {
53
+ $held = 'AUTHORIZED' === $this->get_payment()->getCardDetails()->getStatus();
54
+ }
55
+
56
+ return $held;
57
+ }
58
+
59
+
60
+ /** Getter methods ************************************************************************************************/
61
+
62
+
63
+ /**
64
+ * Gets the authorization code.
65
+ *
66
+ * @since 2.2.0
67
+ *
68
+ * @return string
69
+ */
70
+ public function get_authorization_code() {
71
+
72
+ return $this->get_payment() ? $this->get_payment()->getId() : '';
73
+ }
74
+
75
+
76
+ /**
77
+ * Gets the transaction (payment) ID.
78
+ *
79
+ * @since 2.2.0
80
+ *
81
+ * @return string
82
+ */
83
+ public function get_transaction_id() {
84
+
85
+ return $this->get_payment() ? $this->get_payment()->getId() : '';
86
+ }
87
+
88
+
89
+
90
+ /**
91
+ * Gets the location ID.
92
+ *
93
+ * @since 2.2.0
94
+ *
95
+ * @return string
96
+ */
97
+ public function get_location_id() {
98
+
99
+ return $this->get_payment() ? $this->get_payment()->getLocationId() : '';
100
+ }
101
+
102
+
103
+ /**
104
+ * Gets the Square order ID, if any.
105
+ *
106
+ * @since 2.2.0
107
+ *
108
+ * @return string
109
+ */
110
+ public function get_square_order_id() {
111
+
112
+ return $this->get_payment() ? $this->get_payment()->getOrderId() : '';
113
+ }
114
+
115
+
116
+ /**
117
+ * Gets the Square payment object.
118
+ *
119
+ * @since 2.2.0
120
+ *
121
+ * @return \SquareConnect\Model\Payment|null
122
+ */
123
+ public function get_payment() {
124
+
125
+ return ! $this->has_errors() && $this->get_data()->getPayment() ? $this->get_data()->getPayment() : null;
126
+ }
127
+
128
+
129
+ /**
130
+ * Gets the message to display to the user.
131
+ *
132
+ * @since 2.0.0
133
+ *
134
+ * @return string
135
+ */
136
+ public function get_user_message() {
137
+
138
+ $message_id = '';
139
+
140
+ switch ( $this->get_status_code() ) {
141
+ case 'CARD_DECLINED':
142
+ $message_id = 'card_declined';
143
+ break;
144
+
145
+ case 'INVALID_EXPIRATION':
146
+ $message_id = 'card_expiry_invalid';
147
+ break;
148
+
149
+ case 'VERIFY_AVS_FAILURE':
150
+ $message_id = 'avs_mismatch';
151
+ break;
152
+
153
+ case 'VERIFY_CVV_FAILURE':
154
+ $message_id = 'csc_mismatch';
155
+ break;
156
+ }
157
+
158
+ $helper = new Framework\SV_WC_Payment_Gateway_API_Response_Message_Helper();
159
+
160
+ return $helper->get_user_message( $message_id );
161
+ }
162
+
163
+
164
+ /** No-op methods *************************************************************************************************/
165
+
166
+
167
+ public function get_avs_result() { }
168
+
169
+ public function get_csc_result() { }
170
+
171
+ public function csc_match() { }
172
+
173
+
174
+ }
includes/Gateway/API/Responses/Get_Customer.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -46,8 +46,8 @@ class Get_Customer extends \WooCommerce\Square\Gateway\API\Response implements F
46
  */
47
  public function get_payment_tokens() {
48
 
49
- $cards = $this->get_data() instanceof \SquareConnect\Model\RetrieveCustomerResponse ? $this->get_data()->getCustomer()->getCards() : [];
50
- $tokens = [];
51
 
52
  if ( is_array( $cards ) ) {
53
 
@@ -58,13 +58,13 @@ class Get_Customer extends \WooCommerce\Square\Gateway\API\Response implements F
58
 
59
  $tokens[ $token_id ] = new Framework\SV_WC_Payment_Gateway_Payment_Token(
60
  $token_id,
61
- [
62
  'type' => 'credit_card',
63
  'card_type' => $card_type,
64
  'last_four' => $card->getLast4(),
65
  'exp_month' => $card->getExpMonth(),
66
  'exp_year' => $card->getExpYear(),
67
- ]
68
  );
69
  }
70
  }
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
46
  */
47
  public function get_payment_tokens() {
48
 
49
+ $cards = $this->get_data() instanceof \SquareConnect\Model\RetrieveCustomerResponse ? $this->get_data()->getCustomer()->getCards() : array();
50
+ $tokens = array();
51
 
52
  if ( is_array( $cards ) ) {
53
 
58
 
59
  $tokens[ $token_id ] = new Framework\SV_WC_Payment_Gateway_Payment_Token(
60
  $token_id,
61
+ array(
62
  'type' => 'credit_card',
63
  'card_type' => $card_type,
64
  'last_four' => $card->getLast4(),
65
  'exp_month' => $card->getExpMonth(),
66
  'exp_year' => $card->getExpYear(),
67
+ )
68
  );
69
  }
70
  }
includes/Gateway/API/Responses/Refund.php CHANGED
@@ -23,14 +23,14 @@
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  /**
29
  * The refund API response object.
30
  *
31
  * @since 2.0.0
32
  *
33
- * @method \SquareConnect\Model\CreateRefundResponse get_data()
34
  */
35
  class Refund extends \WooCommerce\Square\Gateway\API\Response {
36
 
@@ -43,8 +43,7 @@ class Refund extends \WooCommerce\Square\Gateway\API\Response {
43
  * @return bool
44
  */
45
  public function transaction_approved() {
46
-
47
- return parent::transaction_approved() && ( 'APPROVED' === $this->get_status_code() || 'PENDING' === $this->get_status_code() );
48
  }
49
 
50
 
23
 
24
  namespace WooCommerce\Square\Gateway\API\Responses;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  /**
29
  * The refund API response object.
30
  *
31
  * @since 2.0.0
32
  *
33
+ * @method \SquareConnect\Model\CreateRefundResponse|GetPaymentRefundResponse get_data()
34
  */
35
  class Refund extends \WooCommerce\Square\Gateway\API\Response {
36
 
43
  * @return bool
44
  */
45
  public function transaction_approved() {
46
+ return parent::transaction_approved() && ( in_array( $this->get_status_code(), array( 'APPROVED', 'COMPLETED', 'PENDING' ), true ) );
 
47
  }
48
 
49
 
includes/Gateway/Card_Handler.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -69,5 +69,114 @@ class Card_Handler extends Framework\SV_WC_Payment_Gateway_Payment_Tokens_Handle
69
  return 'NOT_FOUND' === $response->get_status_code();
70
  }
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
  }
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
69
  return 'NOT_FOUND' === $response->get_status_code();
70
  }
71
 
72
+ /**
73
+ * Gets the available payment tokens for a user as an associative array of
74
+ * payment token to SV_WC_Payment_Gateway_Payment_Token
75
+ *
76
+ * @since 2.2.0
77
+ * @param int $user_id WordPress user identifier, or 0 for guest
78
+ * @param array $args optional arguments, can include
79
+ * `customer_id` - if not provided, this will be looked up based on $user_id
80
+ * `environment_id` - defaults to plugin current environment
81
+ * @return array array of string token to SV_WC_Payment_Gateway_Payment_Token object
82
+ */
83
+ public function get_tokens( $user_id, $args = array() ) {
84
+ // default to current environment
85
+ if ( ! isset( $args['environment_id'] ) ) {
86
+ $args['environment_id'] = $this->get_environment_id();
87
+ }
88
+
89
+ if ( ! isset( $args['customer_id'] ) ) {
90
+ $args['customer_id'] = $this->get_gateway()->get_customer_id( $user_id, array( 'environment_id' => $args['environment_id'] ) );
91
+ }
92
+
93
+ $environment_id = $args['environment_id'];
94
+ $customer_id = $args['customer_id'];
95
+ $transient_key = $this->get_transient_key( $user_id );
96
+
97
+ // return tokens cached during a single request
98
+ if ( isset( $this->tokens[ $environment_id ][ $user_id ] ) ) {
99
+ return $this->tokens[ $environment_id ][ $user_id ];
100
+ }
101
+
102
+ // return tokens cached in transient
103
+ if ( $transient_key ) {
104
+ $this->tokens[ $environment_id ][ $user_id ] = get_transient( $transient_key );
105
+
106
+ if ( false !== $this->tokens[ $environment_id ][ $user_id ] ) {
107
+ return $this->tokens[ $environment_id ][ $user_id ];
108
+ }
109
+ }
110
+
111
+ $this->tokens[ $environment_id ][ $user_id ] = array();
112
+ $tokens = array();
113
+
114
+ // retrieve the datastore persisted tokens first, so we have them for
115
+ // gateways that don't support fetching them over an API, as well as the
116
+ // default token for those that do
117
+ if ( $user_id ) {
118
+ $_tokens = get_user_meta( $user_id, $this->get_user_meta_name( $environment_id ), true );
119
+
120
+ // from database format
121
+ if ( is_array( $_tokens ) ) {
122
+ foreach ( $_tokens as $token => $data ) {
123
+ $tokens[ $token ] = $this->build_token( $token, $data );
124
+ }
125
+ }
126
+
127
+ $this->tokens[ $environment_id ][ $user_id ] = $tokens;
128
+ }
129
+
130
+ if ( $customer_id ) {
131
+ try {
132
+ // retrieve the payment method tokes from the remote API
133
+ $response = $this->get_gateway()->get_api()->get_tokenized_payment_methods( $customer_id );
134
+
135
+ // Only update local tokens when the response has no errors or the customer is not found in Square
136
+ if ( ! $response->has_errors() || 'NOT_FOUND' === $response->get_status_code() ) {
137
+ $this->tokens[ $environment_id ][ $user_id ] = $response->get_payment_tokens();
138
+
139
+ // check for a default from the persisted set, if any
140
+ $default_token = null;
141
+ foreach ( $tokens as $default_token ) {
142
+ if ( $default_token->is_default() ) {
143
+ break;
144
+ }
145
+ }
146
+
147
+ // mark the corresponding token from the API as the default one
148
+ if ( $default_token && $default_token->is_default() && isset( $this->tokens[ $environment_id ][ $user_id ][ $default_token->get_id() ] ) ) {
149
+ $this->tokens[ $environment_id ][ $user_id ][ $default_token->get_id() ]->set_default( true );
150
+ }
151
+
152
+ // merge local token data with remote data, sometimes local data is more robust
153
+ $this->tokens[ $environment_id ][ $user_id ] = $this->merge_token_data( $tokens, $this->tokens[ $environment_id ][ $user_id ] );
154
+
155
+ // persist locally after merging
156
+ $this->update_tokens( $user_id, $this->tokens[ $environment_id ][ $user_id ], $environment_id );
157
+ }
158
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {
159
+
160
+ // communication or other error
161
+ $this->get_gateway()->add_debug_message( $e->getMessage(), 'error' );
162
+
163
+ $this->tokens[ $environment_id ][ $user_id ] = $tokens;
164
+ }
165
+ }
166
+
167
+ // set the payment type image url, if any, for convenience
168
+ foreach ( $this->tokens[ $environment_id ][ $user_id ] as $key => $token ) {
169
+ $this->tokens[ $environment_id ][ $user_id ][ $key ]->set_image_url( $this->get_gateway()->get_payment_method_image_url( $token->is_credit_card() ? $token->get_card_type() : 'echeck' ) );
170
+ }
171
+
172
+ if ( $transient_key ) {
173
+ set_transient( $transient_key, $this->tokens[ $environment_id ][ $user_id ], HOUR_IN_SECONDS );
174
+ }
175
+
176
+ do_action( 'wc_payment_gateway_square_credit_card_payment_tokens_loaded', $this->tokens[ $environment_id ][ $user_id ], $this );
177
+
178
+ return $this->tokens[ $environment_id ][ $user_id ];
179
+ }
180
+
181
 
182
  }
includes/Gateway/Customer_Helper.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -40,8 +40,8 @@ class Customer_Helper {
40
  public static function add_customers( array $customers ) {
41
  global $wpdb;
42
 
43
- $placeholders = [];
44
- $values = [];
45
 
46
  $query = "INSERT INTO {$wpdb->prefix}woocommerce_square_customers (square_id, email_address) VALUES ";
47
 
@@ -81,10 +81,10 @@ class Customer_Helper {
81
 
82
  if ( is_email( $email_address ) ) {
83
 
84
- $params = [
85
  'square_id' => wc_clean( $square_id ),
86
  'email_address' => wc_clean( $email_address ),
87
- ];
88
 
89
  if ( $user_id && is_numeric( $user_id ) ) {
90
  $params['user_id'] = (int) $user_id;
@@ -111,10 +111,12 @@ class Customer_Helper {
111
 
112
  if ( is_email( $email_address ) ) {
113
 
114
- $square_id = $wpdb->get_var( $wpdb->prepare(
115
- "SELECT square_id FROM {$wpdb->prefix}woocommerce_square_customers WHERE email_address = %s",
116
- $email_address
117
- ) );
 
 
118
  }
119
 
120
  return $square_id;
@@ -124,14 +126,16 @@ class Customer_Helper {
124
  public static function get_customers_by_email( $email_address ) {
125
  global $wpdb;
126
 
127
- $square_ids = [];
128
 
129
  if ( is_email( $email_address ) ) {
130
 
131
- $square_ids = $wpdb->get_col( $wpdb->prepare(
132
- "SELECT square_id FROM {$wpdb->prefix}woocommerce_square_customers WHERE email_address = %s",
133
- $email_address
134
- ) );
 
 
135
  }
136
 
137
  return $square_ids;
@@ -149,10 +153,12 @@ class Customer_Helper {
149
  public static function is_customer_indexed( $square_id ) {
150
  global $wpdb;
151
 
152
- $result = $wpdb->get_var( $wpdb->prepare(
153
- "SELECT * FROM {$wpdb->prefix}woocommerce_square_customers WHERE square_id = %s",
154
- $square_id
155
- ) );
 
 
156
 
157
  return (bool) $result;
158
  }
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
40
  public static function add_customers( array $customers ) {
41
  global $wpdb;
42
 
43
+ $placeholders = array();
44
+ $values = array();
45
 
46
  $query = "INSERT INTO {$wpdb->prefix}woocommerce_square_customers (square_id, email_address) VALUES ";
47
 
81
 
82
  if ( is_email( $email_address ) ) {
83
 
84
+ $params = array(
85
  'square_id' => wc_clean( $square_id ),
86
  'email_address' => wc_clean( $email_address ),
87
+ );
88
 
89
  if ( $user_id && is_numeric( $user_id ) ) {
90
  $params['user_id'] = (int) $user_id;
111
 
112
  if ( is_email( $email_address ) ) {
113
 
114
+ $square_id = $wpdb->get_var(
115
+ $wpdb->prepare(
116
+ "SELECT square_id FROM {$wpdb->prefix}woocommerce_square_customers WHERE email_address = %s",
117
+ $email_address
118
+ )
119
+ );
120
  }
121
 
122
  return $square_id;
126
  public static function get_customers_by_email( $email_address ) {
127
  global $wpdb;
128
 
129
+ $square_ids = array();
130
 
131
  if ( is_email( $email_address ) ) {
132
 
133
+ $square_ids = $wpdb->get_col(
134
+ $wpdb->prepare(
135
+ "SELECT square_id FROM {$wpdb->prefix}woocommerce_square_customers WHERE email_address = %s",
136
+ $email_address
137
+ )
138
+ );
139
  }
140
 
141
  return $square_ids;
153
  public static function is_customer_indexed( $square_id ) {
154
  global $wpdb;
155
 
156
+ $result = $wpdb->get_var(
157
+ $wpdb->prepare(
158
+ "SELECT * FROM {$wpdb->prefix}woocommerce_square_customers WHERE square_id = %s",
159
+ $square_id
160
+ )
161
+ );
162
 
163
  return (bool) $result;
164
  }
includes/Gateway/Payment_Form.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -45,7 +45,7 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
45
  */
46
  public function render_supplementary_billing_info() {
47
 
48
- $billing_data = [];
49
  $billing_data_source = null;
50
 
51
  if ( is_checkout_pay_page() ) {
@@ -53,7 +53,6 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
53
  if ( $order = wc_get_order( $this->get_gateway()->get_checkout_pay_page_order_id() ) ) {
54
  $billing_data_source = $order;
55
  }
56
-
57
  } elseif ( WC()->customer && ! is_checkout() ) {
58
 
59
  $billing_data_source = WC()->customer;
@@ -61,27 +60,30 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
61
 
62
  if ( $billing_data_source ) {
63
 
64
- $billing_data = [ 'billing_postcode' => $billing_data_source->get_billing_postcode() ];
65
 
66
  // 3d secure requires the full billing info
67
  if ( $this->get_gateway()->is_3d_secure_enabled() ) {
68
 
69
- $billing_data = array_merge( $billing_data, [
70
- 'billing_first_name' => $billing_data_source->get_billing_first_name(),
71
- 'billing_last_name' => $billing_data_source->get_billing_last_name(),
72
- 'billing_email' => $billing_data_source->get_billing_email(),
73
- 'billing_country' => $billing_data_source->get_billing_country(),
74
- 'billing_address_1' => $billing_data_source->get_billing_address_1(),
75
- 'billing_address_2' => $billing_data_source->get_billing_address_2(),
76
- 'billing_state' => $billing_data_source->get_billing_state(),
77
- 'billing_city' => $billing_data_source->get_billing_city(),
78
- 'billing_phone' => $billing_data_source->get_billing_phone(),
79
- ] );
 
 
 
80
  }
81
  }
82
 
83
  foreach ( $billing_data as $key => $value ) {
84
- echo '<input type="hidden" id="'. esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
85
  }
86
 
87
  if ( is_checkout_pay_page() ) {
@@ -110,14 +112,14 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
110
 
111
  parent::render_payment_fields();
112
 
113
- $fields = [
114
  'card-type',
115
  'last-four',
116
  'exp-month',
117
  'exp-year',
118
  'payment-nonce',
119
  'payment-postcode',
120
- ];
121
 
122
  if ( $this->get_gateway()->is_3d_secure_enabled() ) {
123
  $fields[] = 'buyer-verification-token';
@@ -145,23 +147,23 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
145
  $fields = parent::get_credit_card_fields();
146
 
147
  // Square JS requires a postal code field for the form, but this is pre-filled and hidden
148
- $fields['card-postal-code'] = [
149
  'id' => 'wc-' . $this->get_gateway()->get_id_dasherized() . '-postal-code',
150
  'label' => __( 'Postal code', 'woocommerce-square' ),
151
- 'class' => [ 'form-row-wide' ],
152
  'required' => true,
153
- 'input_class' => [ 'js-sv-wc-payment-gateway-credit-card-form-input', 'js-sv-wc-payment-gateway-credit-card-form-postal-code' ],
154
- ];
155
 
156
- foreach ( [ 'card-number', 'card-expiry', 'card-csc', 'card-postal-code' ] as $field_key ) {
157
 
158
  if ( isset( $fields[ $field_key ] ) ) {
159
 
160
  // parent div classes - contains both the label and hosted field container div
161
- $fields[ $field_key ]['class'] = array_merge( $fields[ $field_key ]['class'], [ "wc-{$this->get_gateway()->get_id_dasherized()}-{$field_key}-parent", "wc-{$this->get_gateway()->get_id_dasherized()}-hosted-field-parent" ] );
162
 
163
  // hosted field container classes - contains the iframe element
164
- $fields[ $field_key ]['input_class'] = array_merge( $fields[ $field_key ]['input_class'], [ "wc-{$this->get_gateway()->get_id_dasherized()}-hosted-field-{$field_key}", "wc-{$this->get_gateway()->get_id_dasherized()}-hosted-field" ] );
165
  }
166
  }
167
 
@@ -180,7 +182,12 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
180
 
181
  ?>
182
  <div class="form-row <?php echo implode( ' ', array_map( 'sanitize_html_class', $field['class'] ) ); ?>">
183
- <label for="<?php echo esc_attr( $field['id'] ) . '-hosted'; ?>"><?php echo esc_html( $field['label'] ); if ( $field['required'] ) : ?><abbr class="required" title="required">&nbsp;*</abbr><?php endif; ?></label>
 
 
 
 
 
184
  <div id="<?php echo esc_attr( $field['id'] ) . '-hosted'; ?>" class="<?php echo implode( ' ', array_map( 'sanitize_html_class', $field['input_class'] ) ); ?>" data-placeholder="<?php echo isset( $field['placeholder'] ) ? esc_attr( $field['placeholder'] ) : ''; ?>"></div>
185
  </div>
186
  <?php
@@ -194,7 +201,7 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
194
  */
195
  public function render_js() {
196
 
197
- $args = [
198
  'application_id' => $this->get_gateway()->get_application_id(),
199
  'ajax_log_nonce' => wp_create_nonce( 'wc_' . $this->get_gateway()->get_id() . '_log_js_data' ),
200
  'ajax_url' => admin_url( 'admin-ajax.php' ),
@@ -209,30 +216,32 @@ class Payment_Form extends Framework\SV_WC_Payment_Gateway_Payment_Form {
209
  'is_add_payment_method_page' => is_add_payment_method_page(),
210
  'location_id' => wc_square()->get_settings_handler()->get_location_id(),
211
  'logging_enabled' => $this->get_gateway()->debug_log(),
212
- ];
 
 
213
 
214
  // map the unique square card type string to our framework standards
215
- $square_card_types = [
216
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_MASTERCARD => 'masterCard',
217
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_AMEX => 'americanExpress',
218
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_DINERSCLUB => 'discoverDiners',
219
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_JCB => 'JCB',
220
- ];
221
 
222
- $card_types = is_array( $this->get_gateway()->get_card_types() ) ? $this->get_gateway()->get_card_types() : [];
223
 
224
- $framework_card_types = array_map( [ Framework\SV_WC_Payment_Gateway_Helper::class, 'normalize_card_type' ], $card_types );
225
  $square_card_types = array_merge( array_combine( $framework_card_types, $framework_card_types ), $square_card_types );
226
 
227
  $args['enabled_card_types'] = $framework_card_types;
228
  $args['square_card_types'] = array_flip( $square_card_types );
229
 
230
- $input_styles = [
231
- [
232
  'backgroundColor' => 'transparent',
233
  'fontSize' => '1.3em',
234
- ]
235
- ];
236
 
237
  /**
238
  * Filters the the Square payment form input styles.
23
 
24
  namespace WooCommerce\Square\Gateway;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
45
  */
46
  public function render_supplementary_billing_info() {
47
 
48
+ $billing_data = array();
49
  $billing_data_source = null;
50
 
51
  if ( is_checkout_pay_page() ) {
53
  if ( $order = wc_get_order( $this->get_gateway()->get_checkout_pay_page_order_id() ) ) {
54
  $billing_data_source = $order;
55
  }
 
56
  } elseif ( WC()->customer && ! is_checkout() ) {
57
 
58
  $billing_data_source = WC()->customer;
60
 
61
  if ( $billing_data_source ) {
62
 
63
+ $billing_data = array( 'billing_postcode' => $billing_data_source->get_billing_postcode() );
64
 
65
  // 3d secure requires the full billing info
66
  if ( $this->get_gateway()->is_3d_secure_enabled() ) {
67
 
68
+ $billing_data = array_merge(
69
+ $billing_data,
70
+ array(
71
+ 'billing_first_name' => $billing_data_source->get_billing_first_name(),
72
+ 'billing_last_name' => $billing_data_source->get_billing_last_name(),
73
+ 'billing_email' => $billing_data_source->get_billing_email(),
74
+ 'billing_country' => $billing_data_source->get_billing_country(),
75
+ 'billing_address_1' => $billing_data_source->get_billing_address_1(),
76
+ 'billing_address_2' => $billing_data_source->get_billing_address_2(),
77
+ 'billing_state' => $billing_data_source->get_billing_state(),
78
+ 'billing_city' => $billing_data_source->get_billing_city(),
79
+ 'billing_phone' => $billing_data_source->get_billing_phone(),
80
+ )
81
+ );
82
  }
83
  }
84
 
85
  foreach ( $billing_data as $key => $value ) {
86
+ echo '<input type="hidden" id="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
87
  }
88
 
89
  if ( is_checkout_pay_page() ) {
112
 
113
  parent::render_payment_fields();
114
 
115
+ $fields = array(
116
  'card-type',
117
  'last-four',
118
  'exp-month',
119
  'exp-year',
120
  'payment-nonce',
121
  'payment-postcode',
122
+ );
123
 
124
  if ( $this->get_gateway()->is_3d_secure_enabled() ) {
125
  $fields[] = 'buyer-verification-token';
147
  $fields = parent::get_credit_card_fields();
148
 
149
  // Square JS requires a postal code field for the form, but this is pre-filled and hidden
150
+ $fields['card-postal-code'] = array(
151
  'id' => 'wc-' . $this->get_gateway()->get_id_dasherized() . '-postal-code',
152
  'label' => __( 'Postal code', 'woocommerce-square' ),
153
+ 'class' => array( 'form-row-wide' ),
154
  'required' => true,
155
+ 'input_class' => array( 'js-sv-wc-payment-gateway-credit-card-form-input', 'js-sv-wc-payment-gateway-credit-card-form-postal-code' ),
156
+ );
157
 
158
+ foreach ( array( 'card-number', 'card-expiry', 'card-csc', 'card-postal-code' ) as $field_key ) {
159
 
160
  if ( isset( $fields[ $field_key ] ) ) {
161
 
162
  // parent div classes - contains both the label and hosted field container div
163
+ $fields[ $field_key ]['class'] = array_merge( $fields[ $field_key ]['class'], array( "wc-{$this->get_gateway()->get_id_dasherized()}-{$field_key}-parent", "wc-{$this->get_gateway()->get_id_dasherized()}-hosted-field-parent" ) );
164
 
165
  // hosted field container classes - contains the iframe element
166
+ $fields[ $field_key ]['input_class'] = array_merge( $fields[ $field_key ]['input_class'], array( "wc-{$this->get_gateway()->get_id_dasherized()}-hosted-field-{$field_key}", "wc-{$this->get_gateway()->get_id_dasherized()}-hosted-field" ) );
167
  }
168
  }
169
 
182
 
183
  ?>
184
  <div class="form-row <?php echo implode( ' ', array_map( 'sanitize_html_class', $field['class'] ) ); ?>">
185
+ <label for="<?php echo esc_attr( $field['id'] ) . '-hosted'; ?>">
186
+ <?php
187
+ echo esc_html( $field['label'] );
188
+ if ( $field['required'] ) :
189
+ ?>
190
+ <abbr class="required" title="required">&nbsp;*</abbr> <?php endif; ?></label>
191
  <div id="<?php echo esc_attr( $field['id'] ) . '-hosted'; ?>" class="<?php echo implode( ' ', array_map( 'sanitize_html_class', $field['input_class'] ) ); ?>" data-placeholder="<?php echo isset( $field['placeholder'] ) ? esc_attr( $field['placeholder'] ) : ''; ?>"></div>
192
  </div>
193
  <?php
201
  */
202
  public function render_js() {
203
 
204
+ $args = array(
205
  'application_id' => $this->get_gateway()->get_application_id(),
206
  'ajax_log_nonce' => wp_create_nonce( 'wc_' . $this->get_gateway()->get_id() . '_log_js_data' ),
207
  'ajax_url' => admin_url( 'admin-ajax.php' ),
216
  'is_add_payment_method_page' => is_add_payment_method_page(),
217
  'location_id' => wc_square()->get_settings_handler()->get_location_id(),
218
  'logging_enabled' => $this->get_gateway()->debug_log(),
219
+ 'ajax_wc_checkout_validate_nonce' => wp_create_nonce( 'wc_' . $this->get_gateway()->get_id() . '_checkout_validate' ),
220
+ 'is_manual_order_payment' => is_checkout() && is_wc_endpoint_url( 'order-pay' ),
221
+ );
222
 
223
  // map the unique square card type string to our framework standards
224
+ $square_card_types = array(
225
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_MASTERCARD => 'masterCard',
226
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_AMEX => 'americanExpress',
227
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_DINERSCLUB => 'discoverDiners',
228
  Framework\SV_WC_Payment_Gateway_Helper::CARD_TYPE_JCB => 'JCB',
229
+ );
230
 
231
+ $card_types = is_array( $this->get_gateway()->get_card_types() ) ? $this->get_gateway()->get_card_types() : array();
232
 
233
+ $framework_card_types = array_map( array( Framework\SV_WC_Payment_Gateway_Helper::class, 'normalize_card_type' ), $card_types );
234
  $square_card_types = array_merge( array_combine( $framework_card_types, $framework_card_types ), $square_card_types );
235
 
236
  $args['enabled_card_types'] = $framework_card_types;
237
  $args['square_card_types'] = array_flip( $square_card_types );
238
 
239
+ $input_styles = array(
240
+ array(
241
  'backgroundColor' => 'transparent',
242
  'fontSize' => '1.3em',
243
+ ),
244
+ );
245
 
246
  /**
247
  * Filters the the Square payment form input styles.
includes/Handlers/Background_Job.php CHANGED
@@ -30,7 +30,7 @@ use WooCommerce\Square\Sync\Manual_Synchronization;
30
  use WooCommerce\Square\Sync\Product_Import;
31
  use WooCommerce\Square\Sync\Records;
32
 
33
- defined( 'ABSPATH' ) or exit;
34
 
35
  /**
36
  * Product and Inventory Synchronization handler class.
@@ -62,16 +62,19 @@ class Background_Job extends Framework\SV_WP_Background_Job_Handler {
62
 
63
  $this->maybe_increase_time_limit();
64
 
65
- add_action( "{$this->identifier}_job_complete", [ $this, 'job_complete' ] );
66
- add_action( "{$this->identifier}_job_failed", [ $this, 'job_failed' ] );
67
- add_filter( "{$this->identifier}_default_time_limit", [ $this, 'set_default_time_limit' ] );
68
 
69
  // ensures the queue lock time never expires before our timeout does
70
- add_filter( "{$this->identifier}_queue_lock_time", function( $lock_time ) {
 
 
71
 
72
- return $this->set_default_time_limit( $lock_time ) + 10;
73
 
74
- } );
 
75
  }
76
 
77
 
@@ -87,17 +90,23 @@ class Background_Job extends Framework\SV_WP_Background_Job_Handler {
87
 
88
  $sor = wc_square()->get_settings_handler()->get_system_of_record();
89
 
90
- return parent::create_job( wp_parse_args( $attrs, [
91
- 'action' => '', // job action
92
- 'catalog_processed' => false, // whether the Square catalog has been processed
93
- 'cursor' => '', // job advancement position
94
- 'manual' => false, // whether it's a sync job triggered manually
95
- 'percentage' => 0, // percentage completed
96
- 'product_ids' => [], // products to process
97
- 'processed_product_ids' => [], // products processed
98
- 'skipped_products' => [], // remote product IDs that were skipped
99
- 'system_of_record' => $sor, // system of record used
100
- ] ) );
 
 
 
 
 
 
101
  }
102
 
103
 
@@ -169,7 +178,7 @@ class Background_Job extends Framework\SV_WP_Background_Job_Handler {
169
 
170
  if ( $job instanceof Job ) {
171
  $current_user_id = get_current_user_id();
172
- $job = $job->run();
173
  wp_set_current_user( $current_user_id );
174
  }
175
 
@@ -203,10 +212,12 @@ class Background_Job extends Framework\SV_WP_Background_Job_Handler {
203
  */
204
  public function job_failed( $job ) {
205
 
206
- Records::set_record( [
207
- 'type' => 'alert',
208
- 'message' => 'Sync failed. Please try again',
209
- ] );
 
 
210
 
211
  wc_square()->get_email_handler()->get_sync_completed_email()->trigger( $job );
212
  }
@@ -336,7 +347,7 @@ class Background_Job extends Framework\SV_WP_Background_Job_Handler {
336
  'name' => __( 'Clear Square Sync', 'woocommerce-square' ),
337
  'button' => __( 'Clear', 'woocommerce-square' ),
338
  'desc' => __( 'This tool will clear any ongoing Square product syncs.', 'woocommerce-square' ),
339
- 'callback' => [ $this, 'run_clear_background_jobs' ],
340
  );
341
 
342
  return $tools;
30
  use WooCommerce\Square\Sync\Product_Import;
31
  use WooCommerce\Square\Sync\Records;
32
 
33
+ defined( 'ABSPATH' ) || exit;
34
 
35
  /**
36
  * Product and Inventory Synchronization handler class.
62
 
63
  $this->maybe_increase_time_limit();
64
 
65
+ add_action( "{$this->identifier}_job_complete", array( $this, 'job_complete' ) );
66
+ add_action( "{$this->identifier}_job_failed", array( $this, 'job_failed' ) );
67
+ add_filter( "{$this->identifier}_default_time_limit", array( $this, 'set_default_time_limit' ) );
68
 
69
  // ensures the queue lock time never expires before our timeout does
70
+ add_filter(
71
+ "{$this->identifier}_queue_lock_time",
72
+ function( $lock_time ) {
73
 
74
+ return $this->set_default_time_limit( $lock_time ) + 10;
75
 
76
+ }
77
+ );
78
  }
79
 
80
 
90
 
91
  $sor = wc_square()->get_settings_handler()->get_system_of_record();
92
 
93
+ return parent::create_job(
94
+ wp_parse_args(
95
+ $attrs,
96
+ array(
97
+ 'action' => '', // job action
98
+ 'catalog_processed' => false, // whether the Square catalog has been processed
99
+ 'cursor' => '', // job advancement position
100
+ 'manual' => false, // whether it's a sync job triggered manually
101
+ 'percentage' => 0, // percentage completed
102
+ 'product_ids' => array(), // products to process
103
+ 'processed_product_ids' => array(), // newly imported products processed
104
+ 'updated_product_ids' => array(), // updated products processed
105
+ 'skipped_products' => array(), // remote product IDs that were skipped
106
+ 'system_of_record' => $sor, // system of record used
107
+ )
108
+ )
109
+ );
110
  }
111
 
112
 
178
 
179
  if ( $job instanceof Job ) {
180
  $current_user_id = get_current_user_id();
181
+ $job = $job->run();
182
  wp_set_current_user( $current_user_id );
183
  }
184
 
212
  */
213
  public function job_failed( $job ) {
214
 
215
+ Records::set_record(
216
+ array(
217
+ 'type' => 'alert',
218
+ 'message' => 'Sync failed. Please try again',
219
+ )
220
+ );
221
 
222
  wc_square()->get_email_handler()->get_sync_completed_email()->trigger( $job );
223
  }
347
  'name' => __( 'Clear Square Sync', 'woocommerce-square' ),
348
  'button' => __( 'Clear', 'woocommerce-square' ),
349
  'desc' => __( 'This tool will clear any ongoing Square product syncs.', 'woocommerce-square' ),
350
+ 'callback' => array( $this, 'run_clear_background_jobs' ),
351
  );
352
 
353
  return $tools;
includes/Handlers/Category.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\Handlers;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Category handler class.
@@ -51,7 +51,7 @@ class Category {
51
  */
52
  public static function get_map() {
53
 
54
- return get_option( self::CATEGORY_MAP_META_KEY, [] );
55
  }
56
 
57
 
@@ -86,7 +86,7 @@ class Category {
86
  return $map[ $category_id ];
87
  }
88
 
89
- return [];
90
  }
91
 
92
 
@@ -137,10 +137,10 @@ class Category {
137
 
138
  $map = self::get_map();
139
 
140
- $map[ $category_id ] = [
141
  'square_id' => $square_id,
142
  'square_version' => $square_version,
143
- ];
144
 
145
  self::update_map( $map );
146
 
@@ -182,9 +182,8 @@ class Category {
182
  $category_id = isset( $inserted_term['term_id'] ) ? $inserted_term['term_id'] : null;
183
  }
184
 
185
-
186
  if ( $category_id ) {
187
- wp_update_term( $category_id, 'product_cat', [ 'name' => $name ] );
188
  self::update_square_meta( $category_id, $id, $version );
189
  }
190
 
@@ -219,14 +218,18 @@ class Category {
219
  public static function get_category_id_by_square_id( $square_id ) {
220
  global $wpdb;
221
 
222
- $query = $wpdb->prepare( "
 
223
  SELECT t.term_id FROM {$wpdb->prefix}terms AS t
224
  LEFT JOIN {$wpdb->prefix}term_taxonomy AS tt ON t.term_id = tt.term_id
225
  LEFT JOIN {$wpdb->prefix}termmeta AS tm ON t.term_id = tm.term_id
226
  WHERE tt.taxonomy = 'product_cat'
227
  AND tm.meta_key = '%s'
228
  AND tm.meta_value = '%s'
229
- ", self::SQUARE_ID_META_KEY, $square_id );
 
 
 
230
 
231
  return $wpdb->get_var( $query );
232
  }
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Category handler class.
51
  */
52
  public static function get_map() {
53
 
54
+ return get_option( self::CATEGORY_MAP_META_KEY, array() );
55
  }
56
 
57
 
86
  return $map[ $category_id ];
87
  }
88
 
89
+ return array();
90
  }
91
 
92
 
137
 
138
  $map = self::get_map();
139
 
140
+ $map[ $category_id ] = array(
141
  'square_id' => $square_id,
142
  'square_version' => $square_version,
143
+ );
144
 
145
  self::update_map( $map );
146
 
182
  $category_id = isset( $inserted_term['term_id'] ) ? $inserted_term['term_id'] : null;
183
  }
184
 
 
185
  if ( $category_id ) {
186
+ wp_update_term( $category_id, 'product_cat', array( 'name' => $name ) );
187
  self::update_square_meta( $category_id, $id, $version );
188
  }
189
 
218
  public static function get_category_id_by_square_id( $square_id ) {
219
  global $wpdb;
220
 
221
+ $query = $wpdb->prepare(
222
+ "
223
  SELECT t.term_id FROM {$wpdb->prefix}terms AS t
224
  LEFT JOIN {$wpdb->prefix}term_taxonomy AS tt ON t.term_id = tt.term_id
225
  LEFT JOIN {$wpdb->prefix}termmeta AS tm ON t.term_id = tm.term_id
226
  WHERE tt.taxonomy = 'product_cat'
227
  AND tm.meta_key = '%s'
228
  AND tm.meta_value = '%s'
229
+ ",
230
+ self::SQUARE_ID_META_KEY,
231
+ $square_id
232
+ );
233
 
234
  return $wpdb->get_var( $query );
235
  }
includes/Handlers/Connection.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Handlers;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model\ListCustomersResponse;
@@ -75,15 +75,15 @@ class Connection {
75
  */
76
  protected function add_hooks() {
77
 
78
- add_action( 'admin_action_wc_' . $this->get_plugin()->get_id() . '_connected', [ $this, 'handle_connected' ] );
79
 
80
- add_action( 'admin_action_wc_' . $this->get_plugin()->get_id() . '_disconnect', [ $this, 'handle_disconnect' ] );
81
 
82
  // refresh the connection, triggered by Action Scheduler
83
- add_action( 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', [ $this, 'refresh_connection' ] );
84
 
85
  // index customers, triggered by Action Scheduler
86
- add_action( 'wc_' . $this->get_plugin()->get_id() . '_index_customers', [ $this, 'index_customers' ] );
87
  }
88
 
89
 
@@ -107,13 +107,16 @@ class Connection {
107
 
108
  if ( empty( $access_token ) ) {
109
  $this->get_plugin()->log( 'Error: No access token was received.' );
110
- add_action( 'admin_notices', function () {
111
- ?>
112
- <div class="notice notice-error is-dismissible">
113
- <p><?php _e( 'Square Error: We could not connect to Square. No access token was given.!', 'woocommerce-square' ); ?></p>
114
- </div>
115
- <?php
116
- });
 
 
 
117
  return;
118
  }
119
 
@@ -228,7 +231,7 @@ class Connection {
228
  // Make sure that all refresh actions are cancelled before scheduling it.
229
  $this->unschedule_refresh();
230
 
231
- as_schedule_single_action( time() + $interval, 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', [], $this->get_plugin()->get_id() );
232
  }
233
 
234
 
@@ -257,12 +260,12 @@ class Connection {
257
  return;
258
  }
259
 
260
- $request = [
261
- 'body' => [
262
  'token' => $this->get_plugin()->get_settings_handler()->get_refresh_token(),
263
- ],
264
  'timeout' => 45,
265
- ];
266
 
267
  // make the request
268
  $response = wp_remote_post( $this->get_refresh_url(), $request );
@@ -288,7 +291,7 @@ class Connection {
288
  $this->get_plugin()->get_settings_handler()->update_access_token( $response->get_token() );
289
 
290
  // In case square updates the refresh token.
291
- if( $response->get_refresh_token() ) {
292
  $this->get_plugin()->get_settings_handler()->update_refresh_token( $response->get_refresh_token() );
293
  $this->get_plugin()->log( 'Connection successfully refreshed.' );
294
  }
@@ -314,7 +317,7 @@ class Connection {
314
  * @since 2.0.0
315
  */
316
  protected function unschedule_refresh() {
317
- as_unschedule_all_actions( 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', [], $this->get_plugin()->get_id() );
318
  }
319
 
320
 
@@ -343,10 +346,8 @@ class Connection {
343
  $this->schedule_customer_index( $response->get_data()->getCursor() );
344
  }
345
  }
346
-
347
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
348
 
349
-
350
  }
351
  }
352
 
@@ -360,8 +361,8 @@ class Connection {
360
  */
361
  protected function schedule_customer_index( $cursor = '' ) {
362
 
363
- if ( false === as_next_scheduled_action( 'wc_' . $this->get_plugin()->get_id() . '_index_customers', [ $cursor ], $this->get_plugin()->get_id() ) ) {
364
- as_schedule_single_action( time(), 'wc_' . $this->get_plugin()->get_id() . '_index_customers', [ $cursor ], $this->get_plugin()->get_id() );
365
  }
366
  }
367
 
@@ -438,10 +439,10 @@ class Connection {
438
  $action = 'wc_' . $this->get_plugin()->get_id() . '_connected';
439
  $redirect_url = wp_nonce_url( add_query_arg( 'action', $action, admin_url() ), $action );
440
 
441
- $args = [
442
  'redirect' => urlencode( urlencode( $redirect_url ) ),
443
  'scopes' => implode( ',', $this->get_scopes() ),
444
- ];
445
 
446
  return add_query_arg( $args, $url );
447
  }
@@ -484,7 +485,7 @@ class Connection {
484
  */
485
  protected function get_scopes() {
486
 
487
- $scopes = [
488
  'MERCHANT_PROFILE_READ',
489
  'PAYMENTS_READ',
490
  'PAYMENTS_WRITE',
@@ -497,7 +498,7 @@ class Connection {
497
  'ITEMS_WRITE',
498
  'INVENTORY_READ',
499
  'INVENTORY_WRITE',
500
- ];
501
 
502
  return (array) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_connection_scopes', $scopes );
503
  }
23
 
24
  namespace WooCommerce\Square\Handlers;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use SquareConnect\Model\ListCustomersResponse;
75
  */
76
  protected function add_hooks() {
77
 
78
+ add_action( 'admin_action_wc_' . $this->get_plugin()->get_id() . '_connected', array( $this, 'handle_connected' ) );
79
 
80
+ add_action( 'admin_action_wc_' . $this->get_plugin()->get_id() . '_disconnect', array( $this, 'handle_disconnect' ) );
81
 
82
  // refresh the connection, triggered by Action Scheduler
83
+ add_action( 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', array( $this, 'refresh_connection' ) );
84
 
85
  // index customers, triggered by Action Scheduler
86
+ add_action( 'wc_' . $this->get_plugin()->get_id() . '_index_customers', array( $this, 'index_customers' ) );
87
  }
88
 
89
 
107
 
108
  if ( empty( $access_token ) ) {
109
  $this->get_plugin()->log( 'Error: No access token was received.' );
110
+ add_action(
111
+ 'admin_notices',
112
+ function () {
113
+ ?>
114
+ <div class="notice notice-error is-dismissible">
115
+ <p><?php _e( 'Square Error: We could not connect to Square. No access token was given.!', 'woocommerce-square' ); ?></p>
116
+ </div>
117
+ <?php
118
+ }
119
+ );
120
  return;
121
  }
122
 
231
  // Make sure that all refresh actions are cancelled before scheduling it.
232
  $this->unschedule_refresh();
233
 
234
+ as_schedule_single_action( time() + $interval, 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', array(), $this->get_plugin()->get_id() );
235
  }
236
 
237
 
260
  return;
261
  }
262
 
263
+ $request = array(
264
+ 'body' => array(
265
  'token' => $this->get_plugin()->get_settings_handler()->get_refresh_token(),
266
+ ),
267
  'timeout' => 45,
268
+ );
269
 
270
  // make the request
271
  $response = wp_remote_post( $this->get_refresh_url(), $request );
291
  $this->get_plugin()->get_settings_handler()->update_access_token( $response->get_token() );
292
 
293
  // In case square updates the refresh token.
294
+ if ( $response->get_refresh_token() ) {
295
  $this->get_plugin()->get_settings_handler()->update_refresh_token( $response->get_refresh_token() );
296
  $this->get_plugin()->log( 'Connection successfully refreshed.' );
297
  }
317
  * @since 2.0.0
318
  */
319
  protected function unschedule_refresh() {
320
+ as_unschedule_all_actions( 'wc_' . $this->get_plugin()->get_id() . '_refresh_connection', array(), $this->get_plugin()->get_id() );
321
  }
322
 
323
 
346
  $this->schedule_customer_index( $response->get_data()->getCursor() );
347
  }
348
  }
 
349
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
350
 
 
351
  }
352
  }
353
 
361
  */
362
  protected function schedule_customer_index( $cursor = '' ) {
363
 
364
+ if ( false === as_next_scheduled_action( 'wc_' . $this->get_plugin()->get_id() . '_index_customers', array( $cursor ), $this->get_plugin()->get_id() ) ) {
365
+ as_schedule_single_action( time(), 'wc_' . $this->get_plugin()->get_id() . '_index_customers', array( $cursor ), $this->get_plugin()->get_id() );
366
  }
367
  }
368
 
439
  $action = 'wc_' . $this->get_plugin()->get_id() . '_connected';
440
  $redirect_url = wp_nonce_url( add_query_arg( 'action', $action, admin_url() ), $action );
441
 
442
+ $args = array(
443
  'redirect' => urlencode( urlencode( $redirect_url ) ),
444
  'scopes' => implode( ',', $this->get_scopes() ),
445
+ );
446
 
447
  return add_query_arg( $args, $url );
448
  }
485
  */
486
  protected function get_scopes() {
487
 
488
+ $scopes = array(
489
  'MERCHANT_PROFILE_READ',
490
  'PAYMENTS_READ',
491
  'PAYMENTS_WRITE',
498
  'ITEMS_WRITE',
499
  'INVENTORY_READ',
500
  'INVENTORY_WRITE',
501
+ );
502
 
503
  return (array) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_connection_scopes', $scopes );
504
  }
includes/Handlers/Email.php CHANGED
@@ -26,7 +26,7 @@ namespace WooCommerce\Square\Handlers;
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\Emails;
28
 
29
- defined( 'ABSPATH' ) or exit;
30
 
31
  /**
32
  * Emails handler class.
@@ -47,8 +47,8 @@ class Email {
47
  */
48
  public function __construct() {
49
  // add email handlers to WooCommerce core
50
- add_action( 'woocommerce_loaded', [ $this, 'init_emails' ] );
51
- add_filter( 'woocommerce_email_classes', [ $this, 'get_email_classes' ] );
52
  }
53
 
54
  /**
@@ -92,7 +92,7 @@ class Email {
92
  * @param \WC_Email[] $emails associative array of email IDs and objects
93
  * @return \WC_Email[]
94
  */
95
- public function get_email_classes( $emails = [] ) {
96
  // init emails if uninitialized
97
  $this->init_emails();
98
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\Emails;
28
 
29
+ defined( 'ABSPATH' ) || exit;
30
 
31
  /**
32
  * Emails handler class.
47
  */
48
  public function __construct() {
49
  // add email handlers to WooCommerce core
50
+ add_action( 'woocommerce_loaded', array( $this, 'init_emails' ) );
51
+ add_filter( 'woocommerce_email_classes', array( $this, 'get_email_classes' ) );
52
  }
53
 
54
  /**
92
  * @param \WC_Email[] $emails associative array of email IDs and objects
93
  * @return \WC_Email[]
94
  */
95
+ public function get_email_classes( $emails = array() ) {
96
  // init emails if uninitialized
97
  $this->init_emails();
98
 
includes/Handlers/Order.php CHANGED
@@ -27,7 +27,7 @@ use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\Plugin;
28
  use WooCommerce\Square\Handlers\Product;
29
 
30
- defined( 'ABSPATH' ) or exit;
31
 
32
  /**
33
  * Order handler class.
@@ -39,16 +39,16 @@ class Order {
39
  /**
40
  * Array of previous stock values.
41
  *
42
- * @var []
43
  */
44
- private $previous_stock = [];
45
 
46
  /**
47
  * Array of product IDs that have been scheduled for sync in this request.
48
  *
49
- * @var []
50
  */
51
- private $products_to_sync = [];
52
 
53
 
54
  /**
@@ -59,16 +59,16 @@ class Order {
59
  public function __construct() {
60
 
61
  // remove Square variation IDs from order item meta
62
- add_action( 'woocommerce_hidden_order_itemmeta', [ $this, 'hide_square_order_item_meta' ] );
63
 
64
  // ADD hooks for stock syncs based on changes from orders not from this gateway
65
- add_action( 'woocommerce_checkout_order_processed', [ $this, 'maybe_sync_stock_for_order_via_other_gateway' ], 10, 3 );
66
 
67
  // Add specific hook for paypal IPN callback
68
- add_action( 'valid-paypal-standard-ipn-request', [ $this, 'maybe_sync_stock_for_order_via_paypal' ], 10, 1 );
69
 
70
  // ADD hooks to listen to refunds on orders from other gateways.
71
- add_action( 'woocommerce_order_refunded', [ $this, 'maybe_sync_stock_for_refund_from_other_gateway' ], 10, 2 );
72
  }
73
 
74
 
@@ -97,11 +97,11 @@ class Order {
97
  * @param array $posted values returned from PayPal Standard IPN callback.
98
  */
99
  public function maybe_sync_stock_for_order_via_paypal( $posted ) {
100
- if ( empty( $posted[ 'custom' ] ) ) {
101
  return;
102
  }
103
 
104
- $raw_order = json_decode( $posted[ 'custom' ] );
105
  if ( empty( $raw_order->order_id ) ) {
106
  return;
107
  }
@@ -152,10 +152,10 @@ class Order {
152
 
153
  $this->cache_previous_stock( $order );
154
 
155
- add_action( 'woocommerce_product_set_stock', [ $this, 'maybe_stage_inventory_updates_for_product' ] );
156
- add_action( 'woocommerce_variation_set_stock', [ $this, 'maybe_stage_inventory_updates_for_product' ] );
157
 
158
- add_action( 'shutdown', [ $this, 'maybe_sync_staged_inventory_updates' ] );
159
  }
160
 
161
  /**
@@ -225,7 +225,7 @@ class Order {
225
  */
226
  public function maybe_sync_staged_inventory_updates() {
227
 
228
- $inventory_adjustments = [];
229
 
230
  foreach ( $this->products_to_sync as $product_id => $adjustment ) {
231
 
@@ -272,8 +272,8 @@ class Order {
272
  return;
273
  }
274
 
275
- $refund = new \WC_Order_Refund( $refund_id );
276
- $inventory_adjustments = [];
277
  foreach ( $refund->get_items() as $item ) {
278
 
279
  if ( 'line_item' !== $item->get_type() ) {
@@ -285,7 +285,7 @@ class Order {
285
  continue;
286
  }
287
 
288
- $adjustment = -1 * ( $item->get_quantity() ); // we want a positive value to increase the stock and a negative number to decrease it.
289
  $inventory_adjustment = Product::get_inventory_change_adjustment_type( $product, $adjustment );
290
 
291
  if ( empty( $inventory_adjustment ) ) {
27
  use WooCommerce\Square\Plugin;
28
  use WooCommerce\Square\Handlers\Product;
29
 
30
+ defined( 'ABSPATH' ) || exit;
31
 
32
  /**
33
  * Order handler class.
39
  /**
40
  * Array of previous stock values.
41
  *
42
+ * @var array
43
  */
44
+ private $previous_stock = array();
45
 
46
  /**
47
  * Array of product IDs that have been scheduled for sync in this request.
48
  *
49
+ * @var array
50
  */
51
+ private $products_to_sync = array();
52
 
53
 
54
  /**
59
  public function __construct() {
60
 
61
  // remove Square variation IDs from order item meta
62
+ add_action( 'woocommerce_hidden_order_itemmeta', array( $this, 'hide_square_order_item_meta' ) );
63
 
64
  // ADD hooks for stock syncs based on changes from orders not from this gateway
65
+ add_action( 'woocommerce_checkout_order_processed', array( $this, 'maybe_sync_stock_for_order_via_other_gateway' ), 10, 3 );
66
 
67
  // Add specific hook for paypal IPN callback
68
+ add_action( 'valid-paypal-standard-ipn-request', array( $this, 'maybe_sync_stock_for_order_via_paypal' ), 10, 1 );
69
 
70
  // ADD hooks to listen to refunds on orders from other gateways.
71
+ add_action( 'woocommerce_order_refunded', array( $this, 'maybe_sync_stock_for_refund_from_other_gateway' ), 10, 2 );
72
  }
73
 
74
 
97
  * @param array $posted values returned from PayPal Standard IPN callback.
98
  */
99
  public function maybe_sync_stock_for_order_via_paypal( $posted ) {
100
+ if ( empty( $posted['custom'] ) ) {
101
  return;
102
  }
103
 
104
+ $raw_order = json_decode( $posted['custom'] );
105
  if ( empty( $raw_order->order_id ) ) {
106
  return;
107
  }
152
 
153
  $this->cache_previous_stock( $order );
154
 
155
+ add_action( 'woocommerce_product_set_stock', array( $this, 'maybe_stage_inventory_updates_for_product' ) );
156
+ add_action( 'woocommerce_variation_set_stock', array( $this, 'maybe_stage_inventory_updates_for_product' ) );
157
 
158
+ add_action( 'shutdown', array( $this, 'maybe_sync_staged_inventory_updates' ) );
159
  }
160
 
161
  /**
225
  */
226
  public function maybe_sync_staged_inventory_updates() {
227
 
228
+ $inventory_adjustments = array();
229
 
230
  foreach ( $this->products_to_sync as $product_id => $adjustment ) {
231
 
272
  return;
273
  }
274
 
275
+ $refund = new \WC_Order_Refund( $refund_id );
276
+ $inventory_adjustments = array();
277
  foreach ( $refund->get_items() as $item ) {
278
 
279
  if ( 'line_item' !== $item->get_type() ) {
285
  continue;
286
  }
287
 
288
+ $adjustment = -1 * ( $item->get_quantity() ); // we want a positive value to increase the stock and a negative number to decrease it.
289
  $inventory_adjustment = Product::get_inventory_change_adjustment_type( $product, $adjustment );
290
 
291
  if ( empty( $inventory_adjustment ) ) {
includes/Handlers/Product.php CHANGED
@@ -28,7 +28,7 @@ use SquareConnect\Model\CatalogObject;
28
  use WooCommerce\Square\Utilities\Money_Utility;
29
  use WooCommerce\Square\Sync\Records;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * Product handler class.
@@ -62,8 +62,8 @@ class Product {
62
  throw new \InvalidArgumentException( 'Type of $catalog_object must be an ITEM' );
63
  }
64
 
65
- $product->update_meta_data( self::SQUARE_ID_META_KEY, $catalog_object->getId() );
66
- $product->update_meta_data( self::SQUARE_VERSION_META_KEY, $catalog_object->getVersion() );
67
  $product->update_meta_data( self::SQUARE_IMAGE_ID_META_KEY, $catalog_object->getImageId() );
68
 
69
  $product->save();
@@ -80,7 +80,7 @@ class Product {
80
  throw new \InvalidArgumentException( 'Type of $catalog_object must be an ITEM_VARIATION' );
81
  }
82
 
83
- $product->update_meta_data( self::SQUARE_VARIATION_ID_META_KEY, $catalog_object->getId() );
84
  $product->update_meta_data( self::SQUARE_VARIATION_VERSION_META_KEY, $catalog_object->getVersion() );
85
 
86
  $product->save();
@@ -144,7 +144,6 @@ class Product {
144
  do_action( 'wc_square_updated_product_variation_from_square', $variation, $catalog_variation );
145
  }
146
  }
147
-
148
  } else {
149
 
150
  $catalog_variation = current( $catalog_variations );
@@ -181,17 +180,19 @@ class Product {
181
  );
182
 
183
  $records = Records::get_records();
184
- foreach ($records as $record) {
185
  if ( $record->get_message() === $message ) {
186
  $is_recorded = true;
187
  }
188
  }
189
 
190
  if ( ! isset( $is_recorded ) ) {
191
- Records::set_record( [
192
- 'type' => 'alert',
193
- 'message' => $message,
194
- ] );
 
 
195
  }
196
  }
197
 
@@ -332,15 +333,17 @@ class Product {
332
  */
333
  public static function init_taxonomies() {
334
 
335
- register_taxonomy( self::SYNCED_WITH_SQUARE_TAXONOMY, [ 'product' ],
336
- [
 
 
337
  'hierarchical' => false,
338
  'update_count_callback' => '_update_generic_term_count',
339
  'show_ui' => false,
340
  'show_in_nav_menus' => false,
341
  'query_var' => is_admin(),
342
  'rewrite' => false,
343
- ]
344
  );
345
  }
346
 
@@ -362,12 +365,12 @@ class Product {
362
 
363
  $product = is_numeric( $product ) ? wc_get_product( $product ) : $product;
364
 
365
- if ( ! $product instanceof \WC_Product || ! in_array( $synced, [ 'yes', 'no' ], true ) ) {
366
  return false;
367
  }
368
 
369
  // ensure only one term is associated with the product at any time
370
- wp_delete_object_term_relationships( $product->get_id(), [ self::SYNCED_WITH_SQUARE_TAXONOMY ] );
371
 
372
  // we have already set the value to "no" above by deleting the term relationship
373
  // so it is safe to return with true.
@@ -375,7 +378,7 @@ class Product {
375
  return true;
376
  }
377
 
378
- $set_term = wp_set_post_terms( $product->get_id(), [ $synced ], self::SYNCED_WITH_SQUARE_TAXONOMY );
379
  $success = is_array( $set_term );
380
 
381
  // Function side effect see phpDoc for details:
@@ -423,7 +426,7 @@ class Product {
423
  }
424
  }
425
 
426
- $terms = wp_get_post_terms( $product->get_id(), self::SYNCED_WITH_SQUARE_TAXONOMY, [ 'fields' => 'names' ] );
427
  }
428
 
429
  return ! empty( $terms ) && 'yes' === $terms[0];
@@ -504,7 +507,7 @@ class Product {
504
 
505
  if ( $product->is_type( 'variable' ) ) {
506
 
507
- $variation_attributes = [];
508
 
509
  foreach ( $product->get_attributes() as $attribute ) {
510
 
@@ -533,30 +536,32 @@ class Product {
533
  private static function get_products_synced_status( $status ) {
534
 
535
  $sync_status_term = get_term_by( 'name', 'yes', self::SYNCED_WITH_SQUARE_TAXONOMY );
536
- $product_ids = [];
537
 
538
- if ( $sync_status_term instanceof \WP_Term && in_array( $status, [ 'yes', 'no' ], true ) ) {
539
 
540
- $tax_query_args = [
541
  'taxonomy' => self::SYNCED_WITH_SQUARE_TAXONOMY,
542
  'field' => 'id',
543
  'terms' => $sync_status_term->term_id,
544
  'include_children' => false,
545
- ];
546
 
547
  if ( 'no' === $status ) {
548
  $tax_query_args['operator'] = 'NOT IN';
549
  }
550
 
551
- $product_ids = get_posts( [
552
- 'post_type' => [ 'product', 'product_variation' ],
553
- 'post_status' => 'any',
554
- 'fields' => 'ids',
555
- 'nopaging' => true,
556
- 'tax_query' => [ $tax_query_args ],
557
- ] );
558
- }
 
559
 
 
560
  return $product_ids;
561
  }
562
 
@@ -678,7 +683,7 @@ class Product {
678
  return self::convert_to_catalog_object( wc_get_product( $parent_id ) );
679
  }
680
 
681
- $variations = [];
682
 
683
  if ( $product->has_child() ) {
684
 
@@ -691,27 +696,26 @@ class Product {
691
  $variations[] = $variation;
692
  }
693
  }
694
-
695
  } else {
696
 
697
  $variation = self::extract_catalog_item_variation_data( $product );
698
- $variations = $variation ? [ $variation ] : [];
699
  }
700
 
701
  if ( empty( $variations ) ) {
702
  return null;
703
  }
704
 
705
- $data = [
706
  'type' => 'ITEM',
707
  'id' => self::get_square_item_id( $product ),
708
  'version' => self::get_square_version( $product ),
709
- 'present_at_location_ids' => [ wc_square()->get_settings_handler()->get_location_id() ],
710
- 'item_data' => [
711
  'name' => $product->get_name(),
712
  'variations' => $variations,
713
- ],
714
- ];
715
 
716
  // TODO: Handle categories
717
 
@@ -729,22 +733,22 @@ class Product {
729
  * @param bool $is_soft_delete whether or not this item data is for a soft-delete
730
  * @return array
731
  */
732
- public static function extract_catalog_item_data( \WC_Product $product, array $variations = [], $is_soft_delete = false ) {
733
 
734
  if ( ! $product ) {
735
  return null;
736
  }
737
 
738
- $data = [
739
  'type' => 'ITEM',
740
  'id' => self::get_square_item_id( $product ),
741
  'version' => self::get_square_version( $product ),
742
- 'present_at_location_ids' => [ wc_square()->get_settings_handler()->get_location_id() ],
743
- 'item_data' => [
744
  'name' => $product->get_name(),
745
  'variations' => $variations,
746
- ],
747
- ];
748
 
749
  $square_category_id = 0;
750
 
@@ -766,7 +770,7 @@ class Product {
766
  if ( $is_soft_delete ) {
767
 
768
  $data['present_at_all_locations'] = false;
769
- $data['present_at_location_ids'] = [];
770
  }
771
 
772
  return $data;
@@ -804,24 +808,24 @@ class Product {
804
 
805
  $item_id = self::get_square_item_id( $parent_product );
806
 
807
- $data = [
808
  'type' => 'ITEM_VARIATION',
809
  'id' => self::get_square_item_variation_id( $product ),
810
  'version' => self::get_square_variation_version( $product ),
811
- 'item_variation_data' => [
812
  'item_id' => $item_id,
813
  'name' => $product->get_name(),
814
  'sku' => $product->get_sku(),
815
  'pricing_type' => 'FIXED_PRICING',
816
  'price_money' => self::price_to_money( $product->get_regular_price() ),
817
  'track_inventory' => true,
818
- ]
819
- ];
820
 
821
  if ( $is_soft_delete ) {
822
 
823
  $data['present_at_all_locations'] = false;
824
- $data['present_at_location_ids'] = [];
825
  }
826
  }
827
 
@@ -1073,41 +1077,44 @@ class Product {
1073
  public static function get_square_meta( $product_ids, $array_key = 'product_id' ) {
1074
  global $wpdb;
1075
 
1076
- $results = $square_meta = [];
1077
 
1078
  if ( ! empty( $product_ids ) ) {
1079
 
1080
- $meta_keys = [
1081
  'square_item_id' => self::SQUARE_ID_META_KEY,
1082
  'square_item_variation_id' => self::SQUARE_VARIATION_ID_META_KEY,
1083
  'square_version' => self::SQUARE_VERSION_META_KEY,
1084
  'square_variation_version' => self::SQUARE_VARIATION_VERSION_META_KEY,
1085
- ];
1086
 
1087
  $array_key = array_key_exists( $array_key, $meta_keys ) ? $array_key : 'product_id';
1088
- $post_ids_in = '(' . implode( ',', array_map( 'absint', array_merge( [ 0 ], $product_ids ) ) ) . ')';
1089
  $meta_key_in = "('" . self::SQUARE_ID_META_KEY . "','" . self::SQUARE_VARIATION_ID_META_KEY . "','" . self::SQUARE_VERSION_META_KEY . "','" . self::SQUARE_VARIATION_VERSION_META_KEY . "')";
1090
- $products_meta = $wpdb->get_results( "
 
1091
  SELECT post_id AS product_id, meta_key, meta_value
1092
  FROM $wpdb->postmeta
1093
  WHERE post_id IN $post_ids_in
1094
  AND meta_key IN $meta_key_in
1095
- ", ARRAY_A );
 
 
1096
 
1097
  foreach ( $products_meta as $post_meta ) {
1098
 
1099
  if ( ! array_key_exists( (string) $post_meta['product_id'], $square_meta ) ) {
1100
- $square_meta[ (string) $post_meta['product_id'] ] = [
1101
  'product_id' => (int) $post_meta['product_id'],
1102
  'square_item_id' => false,
1103
  'square_item_variation_id' => false,
1104
  'square_version' => false,
1105
  'square_variation_version' => false,
1106
- ];
1107
  }
1108
 
1109
  foreach ( $meta_keys as $square_meta_key => $post_meta_key ) {
1110
- if ( isset( $post_meta[ 'meta_key' ] ) && $post_meta_key === $post_meta['meta_key'] ) {
1111
  $square_meta[ $post_meta['product_id'] ][ $square_meta_key ] = $post_meta['meta_value'];
1112
  break;
1113
  }
@@ -1117,9 +1124,9 @@ class Product {
1117
  foreach ( $product_ids as $product_id ) {
1118
 
1119
  // sanity checks: cannot build index without a valid key
1120
- if ( ! array_key_exists( $product_id, $square_meta )
1121
- || ! isset( $square_meta[ $product_id ][ $array_key ] )
1122
- || ! $square_meta[ (string) $product_id ][ $array_key ] ) {
1123
 
1124
  continue;
1125
  }
@@ -1148,12 +1155,12 @@ class Product {
1148
  $product_id = $product_id->get_id();
1149
  }
1150
 
1151
- return [
1152
  'product_id' => $product_id,
1153
  'square_item_id' => self::get_square_item_id( $product_id ),
1154
  'square_item_variation_id' => self::get_square_item_variation_id( $product_id ),
1155
  'square_version' => self::get_square_version( $product_id ),
1156
- ];
1157
  }
1158
 
1159
 
@@ -1193,23 +1200,23 @@ class Product {
1193
 
1194
  case 'item_id':
1195
  self::set_square_item_id( $product_id, $meta_value );
1196
- break;
1197
 
1198
  case 'item_version':
1199
  self::set_square_version( $product_id, $meta_value );
1200
- break;
1201
 
1202
  case 'item_variation_id':
1203
  self::set_square_item_variation_id( $product_id, $meta_value );
1204
- break;
1205
 
1206
  case 'item_variation_version':
1207
  self::set_square_variation_version( $product_id, $meta_value );
1208
- break;
1209
 
1210
  case 'item_image_id':
1211
  self::set_square_image_id( $product_id, $meta_value );
1212
- break;
1213
  }
1214
  }
1215
  }
@@ -1225,25 +1232,27 @@ class Product {
1225
  public static function clear_square_meta( $product_ids ) {
1226
  global $wpdb;
1227
 
1228
- $product_ids = is_array( $product_ids ) ? $product_ids : [ $product_ids ];
1229
 
1230
- $meta_keys = [
1231
  self::SQUARE_ID_META_KEY,
1232
  self::SQUARE_VERSION_META_KEY,
1233
  self::SQUARE_VARIATION_ID_META_KEY,
1234
  self::SQUARE_VARIATION_VERSION_META_KEY,
1235
  self::SQUARE_IMAGE_ID_META_KEY,
1236
- ];
1237
 
1238
  $meta_key_in = '("' . implode( '","', $meta_keys ) . '")';
1239
- $post_ids_in = '(' . implode( ',', array_map( 'absint', array_merge( [ 0 ], $product_ids ) ) ) . ')';
1240
 
1241
- $wpdb->query( "
 
1242
  UPDATE $wpdb->postmeta
1243
  SET meta_value = ''
1244
  WHERE meta_key IN $meta_key_in
1245
  AND post_id IN $post_ids_in;
1246
- " );
 
1247
  }
1248
 
1249
 
@@ -1261,11 +1270,14 @@ class Product {
1261
 
1262
  if ( $product ) {
1263
 
1264
- self::update_square_meta( $product->get_id(), [
1265
- 'item_id' => $remote_product->getId(),
1266
- 'item_version' => $remote_product->getVersion(),
1267
- 'item_image_id' => $remote_product->getImageId(),
1268
- ] );
 
 
 
1269
  }
1270
  }
1271
 
@@ -1284,16 +1296,20 @@ class Product {
1284
 
1285
  if ( $square_variation_id = self::get_square_item_variation_id( $product->get_id(), false ) ) {
1286
 
1287
- $inventory_change = new \SquareConnect\Model\InventoryChange( [
1288
- 'type' => 'PHYSICAL_COUNT',
1289
- 'physical_count' => new \SquareConnect\Model\InventoryPhysicalCount( [
1290
- 'catalog_object_id' => $square_variation_id,
1291
- 'quantity' => '' . max( 0, $product->get_stock_quantity() ),
1292
- 'location_id' => wc_square()->get_settings_handler()->get_location_id(),
1293
- 'state' => 'IN_STOCK',
1294
- 'occurred_at' => date( 'Y-m-d\TH:i:sP' ),
1295
- ] ),
1296
- ] );
 
 
 
 
1297
  }
1298
 
1299
  return $inventory_change;
@@ -1314,7 +1330,7 @@ class Product {
1314
 
1315
  $square_variation_id = self::get_square_item_variation_id( $product->get_id(), false );
1316
 
1317
- if ( empty( $square_variation_id ) || 0 === $adjustment) {
1318
  return null;
1319
  }
1320
 
@@ -1326,17 +1342,21 @@ class Product {
1326
  $to = 'IN_STOCK';
1327
  }
1328
 
1329
- $inventory_change = new \SquareConnect\Model\InventoryChange([
1330
- 'type' => 'ADJUSTMENT',
1331
- 'adjustment' => new \SquareConnect\Model\InventoryAdjustment([
1332
- 'catalog_object_id' => $square_variation_id,
1333
- 'location_id' => wc_square()->get_settings_handler()->get_location_id(),
1334
- 'quantity' => '' . absint( $adjustment ),
1335
- 'from_state' => $from,
1336
- 'to_state' => $to,
1337
- 'occurred_at' => date( 'Y-m-d\TH:i:sP' ),
1338
- ]),
1339
- ]);
 
 
 
 
1340
 
1341
  return $inventory_change;
1342
  }
28
  use WooCommerce\Square\Utilities\Money_Utility;
29
  use WooCommerce\Square\Sync\Records;
30
 
31
+ defined( 'ABSPATH' ) || exit;
32
 
33
  /**
34
  * Product handler class.
62
  throw new \InvalidArgumentException( 'Type of $catalog_object must be an ITEM' );
63
  }
64
 
65
+ $product->update_meta_data( self::SQUARE_ID_META_KEY, $catalog_object->getId() );
66
+ $product->update_meta_data( self::SQUARE_VERSION_META_KEY, $catalog_object->getVersion() );
67
  $product->update_meta_data( self::SQUARE_IMAGE_ID_META_KEY, $catalog_object->getImageId() );
68
 
69
  $product->save();
80
  throw new \InvalidArgumentException( 'Type of $catalog_object must be an ITEM_VARIATION' );
81
  }
82
 
83
+ $product->update_meta_data( self::SQUARE_VARIATION_ID_META_KEY, $catalog_object->getId() );
84
  $product->update_meta_data( self::SQUARE_VARIATION_VERSION_META_KEY, $catalog_object->getVersion() );
85
 
86
  $product->save();
144
  do_action( 'wc_square_updated_product_variation_from_square', $variation, $catalog_variation );
145
  }
146
  }
 
147
  } else {
148
 
149
  $catalog_variation = current( $catalog_variations );
180
  );
181
 
182
  $records = Records::get_records();
183
+ foreach ( $records as $record ) {
184
  if ( $record->get_message() === $message ) {
185
  $is_recorded = true;
186
  }
187
  }
188
 
189
  if ( ! isset( $is_recorded ) ) {
190
+ Records::set_record(
191
+ array(
192
+ 'type' => 'alert',
193
+ 'message' => $message,
194
+ )
195
+ );
196
  }
197
  }
198
 
333
  */
334
  public static function init_taxonomies() {
335
 
336
+ register_taxonomy(
337
+ self::SYNCED_WITH_SQUARE_TAXONOMY,
338
+ array( 'product' ),
339
+ array(
340
  'hierarchical' => false,
341
  'update_count_callback' => '_update_generic_term_count',
342
  'show_ui' => false,
343
  'show_in_nav_menus' => false,
344
  'query_var' => is_admin(),
345
  'rewrite' => false,
346
+ )
347
  );
348
  }
349
 
365
 
366
  $product = is_numeric( $product ) ? wc_get_product( $product ) : $product;
367
 
368
+ if ( ! $product instanceof \WC_Product || ! in_array( $synced, array( 'yes', 'no' ), true ) ) {
369
  return false;
370
  }
371
 
372
  // ensure only one term is associated with the product at any time
373
+ wp_delete_object_term_relationships( $product->get_id(), array( self::SYNCED_WITH_SQUARE_TAXONOMY ) );
374
 
375
  // we have already set the value to "no" above by deleting the term relationship
376
  // so it is safe to return with true.
378
  return true;
379
  }
380
 
381
+ $set_term = wp_set_post_terms( $product->get_id(), array( $synced ), self::SYNCED_WITH_SQUARE_TAXONOMY );
382
  $success = is_array( $set_term );
383
 
384
  // Function side effect see phpDoc for details:
426
  }
427
  }
428
 
429
+ $terms = wp_get_post_terms( $product->get_id(), self::SYNCED_WITH_SQUARE_TAXONOMY, array( 'fields' => 'names' ) );
430
  }
431
 
432
  return ! empty( $terms ) && 'yes' === $terms[0];
507
 
508
  if ( $product->is_type( 'variable' ) ) {
509
 
510
+ $variation_attributes = array();
511
 
512
  foreach ( $product->get_attributes() as $attribute ) {
513
 
536
  private static function get_products_synced_status( $status ) {
537
 
538
  $sync_status_term = get_term_by( 'name', 'yes', self::SYNCED_WITH_SQUARE_TAXONOMY );
539
+ $product_ids = array();
540
 
541
+ if ( $sync_status_term instanceof \WP_Term && in_array( $status, array( 'yes', 'no' ), true ) ) {
542
 
543
+ $tax_query_args = array(
544
  'taxonomy' => self::SYNCED_WITH_SQUARE_TAXONOMY,
545
  'field' => 'id',
546
  'terms' => $sync_status_term->term_id,
547
  'include_children' => false,
548
+ );
549
 
550
  if ( 'no' === $status ) {
551
  $tax_query_args['operator'] = 'NOT IN';
552
  }
553
 
554
+ $product_ids = get_posts(
555
+ array(
556
+ 'post_type' => array( 'product', 'product_variation' ),
557
+ 'post_status' => array( 'private', 'publish' ),
558
+ 'fields' => 'ids',
559
+ 'nopaging' => true,
560
+ 'tax_query' => array( $tax_query_args ),
561
+ )
562
+ );
563
 
564
+ }
565
  return $product_ids;
566
  }
567
 
683
  return self::convert_to_catalog_object( wc_get_product( $parent_id ) );
684
  }
685
 
686
+ $variations = array();
687
 
688
  if ( $product->has_child() ) {
689
 
696
  $variations[] = $variation;
697
  }
698
  }
 
699
  } else {
700
 
701
  $variation = self::extract_catalog_item_variation_data( $product );
702
+ $variations = $variation ? array( $variation ) : array();
703
  }
704
 
705
  if ( empty( $variations ) ) {
706
  return null;
707
  }
708
 
709
+ $data = array(
710
  'type' => 'ITEM',
711
  'id' => self::get_square_item_id( $product ),
712
  'version' => self::get_square_version( $product ),
713
+ 'present_at_location_ids' => array( wc_square()->get_settings_handler()->get_location_id() ),
714
+ 'item_data' => array(
715
  'name' => $product->get_name(),
716
  'variations' => $variations,
717
+ ),
718
+ );
719
 
720
  // TODO: Handle categories
721
 
733
  * @param bool $is_soft_delete whether or not this item data is for a soft-delete
734
  * @return array
735
  */
736
+ public static function extract_catalog_item_data( \WC_Product $product, array $variations = array(), $is_soft_delete = false ) {
737
 
738
  if ( ! $product ) {
739
  return null;
740
  }
741
 
742
+ $data = array(
743
  'type' => 'ITEM',
744
  'id' => self::get_square_item_id( $product ),
745
  'version' => self::get_square_version( $product ),
746
+ 'present_at_location_ids' => array( wc_square()->get_settings_handler()->get_location_id() ),
747
+ 'item_data' => array(
748
  'name' => $product->get_name(),
749
  'variations' => $variations,
750
+ ),
751
+ );
752
 
753
  $square_category_id = 0;
754
 
770
  if ( $is_soft_delete ) {
771
 
772
  $data['present_at_all_locations'] = false;
773
+ $data['present_at_location_ids'] = array();
774
  }
775
 
776
  return $data;
808
 
809
  $item_id = self::get_square_item_id( $parent_product );
810
 
811
+ $data = array(
812
  'type' => 'ITEM_VARIATION',
813
  'id' => self::get_square_item_variation_id( $product ),
814
  'version' => self::get_square_variation_version( $product ),
815
+ 'item_variation_data' => array(
816
  'item_id' => $item_id,
817
  'name' => $product->get_name(),
818
  'sku' => $product->get_sku(),
819
  'pricing_type' => 'FIXED_PRICING',
820
  'price_money' => self::price_to_money( $product->get_regular_price() ),
821
  'track_inventory' => true,
822
+ ),
823
+ );
824
 
825
  if ( $is_soft_delete ) {
826
 
827
  $data['present_at_all_locations'] = false;
828
+ $data['present_at_location_ids'] = array();
829
  }
830
  }
831
 
1077
  public static function get_square_meta( $product_ids, $array_key = 'product_id' ) {
1078
  global $wpdb;
1079
 
1080
+ $results = $square_meta = array();
1081
 
1082
  if ( ! empty( $product_ids ) ) {
1083
 
1084
+ $meta_keys = array(
1085
  'square_item_id' => self::SQUARE_ID_META_KEY,
1086
  'square_item_variation_id' => self::SQUARE_VARIATION_ID_META_KEY,
1087
  'square_version' => self::SQUARE_VERSION_META_KEY,
1088
  'square_variation_version' => self::SQUARE_VARIATION_VERSION_META_KEY,
1089
+ );
1090
 
1091
  $array_key = array_key_exists( $array_key, $meta_keys ) ? $array_key : 'product_id';
1092
+ $post_ids_in = '(' . implode( ',', array_map( 'absint', array_merge( array( 0 ), $product_ids ) ) ) . ')';
1093
  $meta_key_in = "('" . self::SQUARE_ID_META_KEY . "','" . self::SQUARE_VARIATION_ID_META_KEY . "','" . self::SQUARE_VERSION_META_KEY . "','" . self::SQUARE_VARIATION_VERSION_META_KEY . "')";
1094
+ $products_meta = $wpdb->get_results(
1095
+ "
1096
  SELECT post_id AS product_id, meta_key, meta_value
1097
  FROM $wpdb->postmeta
1098
  WHERE post_id IN $post_ids_in
1099
  AND meta_key IN $meta_key_in
1100
+ ",
1101
+ ARRAY_A
1102
+ );
1103
 
1104
  foreach ( $products_meta as $post_meta ) {
1105
 
1106
  if ( ! array_key_exists( (string) $post_meta['product_id'], $square_meta ) ) {
1107
+ $square_meta[ (string) $post_meta['product_id'] ] = array(
1108
  'product_id' => (int) $post_meta['product_id'],
1109
  'square_item_id' => false,
1110
  'square_item_variation_id' => false,
1111
  'square_version' => false,
1112
  'square_variation_version' => false,
1113
+ );
1114
  }
1115
 
1116
  foreach ( $meta_keys as $square_meta_key => $post_meta_key ) {
1117
+ if ( isset( $post_meta['meta_key'] ) && $post_meta_key === $post_meta['meta_key'] ) {
1118
  $square_meta[ $post_meta['product_id'] ][ $square_meta_key ] = $post_meta['meta_value'];
1119
  break;
1120
  }
1124
  foreach ( $product_ids as $product_id ) {
1125
 
1126
  // sanity checks: cannot build index without a valid key
1127
+ if ( ! array_key_exists( $product_id, $square_meta )
1128
+ || ! isset( $square_meta[ $product_id ][ $array_key ] )
1129
+ || ! $square_meta[ (string) $product_id ][ $array_key ] ) {
1130
 
1131
  continue;
1132
  }
1155
  $product_id = $product_id->get_id();
1156
  }
1157
 
1158
+ return array(
1159
  'product_id' => $product_id,
1160
  'square_item_id' => self::get_square_item_id( $product_id ),
1161
  'square_item_variation_id' => self::get_square_item_variation_id( $product_id ),
1162
  'square_version' => self::get_square_version( $product_id ),
1163
+ );
1164
  }
1165
 
1166
 
1200
 
1201
  case 'item_id':
1202
  self::set_square_item_id( $product_id, $meta_value );
1203
+ break;
1204
 
1205
  case 'item_version':
1206
  self::set_square_version( $product_id, $meta_value );
1207
+ break;
1208
 
1209
  case 'item_variation_id':
1210
  self::set_square_item_variation_id( $product_id, $meta_value );
1211
+ break;
1212
 
1213
  case 'item_variation_version':
1214
  self::set_square_variation_version( $product_id, $meta_value );
1215
+ break;
1216
 
1217
  case 'item_image_id':
1218
  self::set_square_image_id( $product_id, $meta_value );
1219
+ break;
1220
  }
1221
  }
1222
  }
1232
  public static function clear_square_meta( $product_ids ) {
1233
  global $wpdb;
1234
 
1235
+ $product_ids = is_array( $product_ids ) ? $product_ids : array( $product_ids );
1236
 
1237
+ $meta_keys = array(
1238
  self::SQUARE_ID_META_KEY,
1239
  self::SQUARE_VERSION_META_KEY,
1240
  self::SQUARE_VARIATION_ID_META_KEY,
1241
  self::SQUARE_VARIATION_VERSION_META_KEY,
1242
  self::SQUARE_IMAGE_ID_META_KEY,
1243
+ );
1244
 
1245
  $meta_key_in = '("' . implode( '","', $meta_keys ) . '")';
1246
+ $post_ids_in = '(' . implode( ',', array_map( 'absint', array_merge( array( 0 ), $product_ids ) ) ) . ')';
1247
 
1248
+ $wpdb->query(
1249
+ "
1250
  UPDATE $wpdb->postmeta
1251
  SET meta_value = ''
1252
  WHERE meta_key IN $meta_key_in
1253
  AND post_id IN $post_ids_in;
1254
+ "
1255
+ );
1256
  }
1257
 
1258
 
1270
 
1271
  if ( $product ) {
1272
 
1273
+ self::update_square_meta(
1274
+ $product->get_id(),
1275
+ array(
1276
+ 'item_id' => $remote_product->getId(),
1277
+ 'item_version' => $remote_product->getVersion(),
1278
+ 'item_image_id' => $remote_product->getImageId(),
1279
+ )
1280
+ );
1281
  }
1282
  }
1283
 
1296
 
1297
  if ( $square_variation_id = self::get_square_item_variation_id( $product->get_id(), false ) ) {
1298
 
1299
+ $inventory_change = new \SquareConnect\Model\InventoryChange(
1300
+ array(
1301
+ 'type' => 'PHYSICAL_COUNT',
1302
+ 'physical_count' => new \SquareConnect\Model\InventoryPhysicalCount(
1303
+ array(
1304
+ 'catalog_object_id' => $square_variation_id,
1305
+ 'quantity' => '' . max( 0, $product->get_stock_quantity() ),
1306
+ 'location_id' => wc_square()->get_settings_handler()->get_location_id(),
1307
+ 'state' => 'IN_STOCK',
1308
+ 'occurred_at' => date( 'Y-m-d\TH:i:sP' ),
1309
+ )
1310
+ ),
1311
+ )
1312
+ );
1313
  }
1314
 
1315
  return $inventory_change;
1330
 
1331
  $square_variation_id = self::get_square_item_variation_id( $product->get_id(), false );
1332
 
1333
+ if ( empty( $square_variation_id ) || 0 === $adjustment ) {
1334
  return null;
1335
  }
1336
 
1342
  $to = 'IN_STOCK';
1343
  }
1344
 
1345
+ $inventory_change = new \SquareConnect\Model\InventoryChange(
1346
+ array(
1347
+ 'type' => 'ADJUSTMENT',
1348
+ 'adjustment' => new \SquareConnect\Model\InventoryAdjustment(
1349
+ array(
1350
+ 'catalog_object_id' => $square_variation_id,
1351
+ 'location_id' => wc_square()->get_settings_handler()->get_location_id(),
1352
+ 'quantity' => '' . absint( $adjustment ),
1353
+ 'from_state' => $from,
1354
+ 'to_state' => $to,
1355
+ 'occurred_at' => date( 'Y-m-d\TH:i:sP' ),
1356
+ )
1357
+ ),
1358
+ )
1359
+ );
1360
 
1361
  return $inventory_change;
1362
  }
includes/Handlers/Product/Woo_SOR.php CHANGED
@@ -78,7 +78,7 @@ class Woo_SOR extends \WooCommerce\Square\Handlers\Product {
78
  $item_data->setCategoryId( $square_category_id );
79
  }
80
 
81
- $catalog_variations = $item_data->getVariations() ?: [];
82
 
83
  // if dealing with a variable product, try and match the variations
84
  if ( $product->is_type( 'variable' ) ) {
@@ -108,7 +108,6 @@ class Woo_SOR extends \WooCommerce\Square\Handlers\Product {
108
  // consider this variation taken care of
109
  unset( $product_variation_ids[ $key ] );
110
  }
111
-
112
  } else {
113
 
114
  unset( $catalog_variations[ $object_key ] );
@@ -125,18 +124,20 @@ class Woo_SOR extends \WooCommerce\Square\Handlers\Product {
125
  continue;
126
  }
127
 
128
- $variation_object = new CatalogObject( [
129
- 'type' => 'ITEM_VARIATION',
130
- 'item_variation_data' => new \SquareConnect\Model\CatalogItemVariation( [
131
- 'item_id' => $catalog_object->getId(),
132
- ] ),
133
- ] );
 
 
 
 
134
 
135
  $catalog_variations[] = self::update_catalog_variation( $variation_object, $product_variation );
136
  }
137
-
138
- // otherwise, we have a simple product
139
- } else {
140
 
141
  if ( ! empty( $catalog_variations ) ) {
142
 
@@ -144,15 +145,19 @@ class Woo_SOR extends \WooCommerce\Square\Handlers\Product {
144
 
145
  } else {
146
 
147
- $variation_object = new CatalogObject( [
148
- 'type' => 'ITEM_VARIATION',
149
- 'item_variation_data' => new \SquareConnect\Model\CatalogItemVariation( [
150
- 'item_id' => $catalog_object->getId(),
151
- ] ),
152
- ] );
 
 
 
 
153
  }
154
 
155
- $catalog_variations = [ self::update_catalog_variation( $variation_object, $product ) ];
156
  }
157
 
158
  $item_data->setVariations( array_values( $catalog_variations ) );
@@ -258,8 +263,8 @@ class Woo_SOR extends \WooCommerce\Square\Handlers\Product {
258
 
259
  $location_id = wc_square()->get_settings_handler()->get_location_id();
260
 
261
- $present_location_ids = $catalog_object->getPresentAtLocationIds() ?: [];
262
- $absent_location_ids = $catalog_object->getAbsentAtLocationIds() ?: [];
263
 
264
  // if trashed, set as absent at our location
265
  if ( $is_delete ) {
@@ -269,9 +274,7 @@ class Woo_SOR extends \WooCommerce\Square\Handlers\Product {
269
  if ( false !== ( $key = array_search( $location_id, $present_location_ids, true ) ) ) {
270
  unset( $present_location_ids[ $key ] );
271
  }
272
-
273
- // otherwise, it's present
274
- } else {
275
 
276
  $present_location_ids[] = $location_id;
277
 
78
  $item_data->setCategoryId( $square_category_id );
79
  }
80
 
81
+ $catalog_variations = $item_data->getVariations() ?: array();
82
 
83
  // if dealing with a variable product, try and match the variations
84
  if ( $product->is_type( 'variable' ) ) {
108
  // consider this variation taken care of
109
  unset( $product_variation_ids[ $key ] );
110
  }
 
111
  } else {
112
 
113
  unset( $catalog_variations[ $object_key ] );
124
  continue;
125
  }
126
 
127
+ $variation_object = new CatalogObject(
128
+ array(
129
+ 'type' => 'ITEM_VARIATION',
130
+ 'item_variation_data' => new \SquareConnect\Model\CatalogItemVariation(
131
+ array(
132
+ 'item_id' => $catalog_object->getId(),
133
+ )
134
+ ),
135
+ )
136
+ );
137
 
138
  $catalog_variations[] = self::update_catalog_variation( $variation_object, $product_variation );
139
  }
140
+ } else { // otherwise, we have a simple product
 
 
141
 
142
  if ( ! empty( $catalog_variations ) ) {
143
 
145
 
146
  } else {
147
 
148
+ $variation_object = new CatalogObject(
149
+ array(
150
+ 'type' => 'ITEM_VARIATION',
151
+ 'item_variation_data' => new \SquareConnect\Model\CatalogItemVariation(
152
+ array(
153
+ 'item_id' => $catalog_object->getId(),
154
+ )
155
+ ),
156
+ )
157
+ );
158
  }
159
 
160
+ $catalog_variations = array( self::update_catalog_variation( $variation_object, $product ) );
161
  }
162
 
163
  $item_data->setVariations( array_values( $catalog_variations ) );
263
 
264
  $location_id = wc_square()->get_settings_handler()->get_location_id();
265
 
266
+ $present_location_ids = $catalog_object->getPresentAtLocationIds() ?: array();
267
+ $absent_location_ids = $catalog_object->getAbsentAtLocationIds() ?: array();
268
 
269
  // if trashed, set as absent at our location
270
  if ( $is_delete ) {
274
  if ( false !== ( $key = array_search( $location_id, $present_location_ids, true ) ) ) {
275
  unset( $present_location_ids[ $key ] );
276
  }
277
+ } else { // otherwise, it's present
 
 
278
 
279
  $present_location_ids[] = $location_id;
280
 
includes/Handlers/Products.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Handlers;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
@@ -42,13 +42,13 @@ class Products {
42
  private $product_errors;
43
 
44
  /** @var array associative array of memoized errors being output for a product at one time */
45
- private $output_errors = [];
46
 
47
  /** @var int[] array of product IDs that have been scheduled for sync in this request */
48
- private $products_to_sync = [];
49
 
50
  /** @var int[] array of product IDs that have been scheduled for deletion in this request */
51
- private $products_to_delete = [];
52
 
53
  /** @var Square\Plugin plugin instance */
54
  private $plugin;
@@ -63,14 +63,14 @@ class Products {
63
  $this->plugin = $plugin;
64
 
65
  // add common errors
66
- $this->product_errors = [
67
  /* translators: Placeholder: %s - product name */
68
  'missing_sku' => __( "Please add an SKU to sync %s with Square. The SKU must match the item's SKU in your Square account.", 'woocommerce-square' ),
69
  /* translators: Placeholder: %s - product name */
70
  'missing_variation_sku' => __( "Please add an SKU to every variation of %s for syncing with Square. Each SKU must match the corresponding item's SKU in your Square account.", 'woocommerce-square' ),
71
  /* translators: Placeholder: %s - product name */
72
  'multiple_attributes' => __( '%s has multiple variation attributes and cannot be synced with Square.', 'woocommerce-square' ),
73
- ];
74
 
75
  // add hooks
76
  $this->add_products_edit_screen_hooks();
@@ -89,18 +89,18 @@ class Products {
89
  private function add_products_edit_screen_hooks() {
90
 
91
  // adds an option to the "Filter by product type" dropdown
92
- add_action( 'restrict_manage_posts', [ $this, 'add_filter_products_synced_with_square_option' ] );
93
  // allow filtering products by sync status by altering results
94
- add_filter( 'request', [ $this, 'filter_products_synced_with_square' ] );
95
 
96
  // prevent copying Square data when duplicating a product automatically
97
- add_action( 'woocommerce_product_duplicate', [ $this, 'handle_product_duplication' ], 20, 2 );
98
 
99
  // handle quick/bulk edit actions in the products edit screen for setting sync status
100
- add_action( 'woocommerce_product_quick_edit_end', [ $this, 'add_quick_edit_inputs' ] );
101
- add_action( 'woocommerce_product_bulk_edit_end', [ $this, 'add_bulk_edit_inputs' ] );
102
- add_action( 'woocommerce_product_quick_edit_save', [ $this, 'set_synced_with_square' ] );
103
- add_action( 'woocommerce_product_bulk_edit_save', [ $this, 'set_synced_with_square' ] );
104
  }
105
 
106
 
@@ -113,14 +113,14 @@ class Products {
113
  */
114
  private function add_product_edit_screen_hooks() {
115
 
116
- add_action( 'woocommerce_variation_options', [ $this, 'add_variation_manage_stock' ] );
117
 
118
  // handle individual products input fields for setting sync status
119
- add_action( 'woocommerce_product_options_general_product_data', [ $this, 'add_product_data_fields' ] );
120
- add_action( 'woocommerce_admin_process_product_object', [ $this, 'process_product_data' ], 20 );
121
- add_action( 'woocommerce_before_product_object_save', [ $this, 'maybe_adjust_square_stock'] );
122
 
123
- add_action( 'admin_notices', [ $this, 'add_notice_product_hidden_from_catalog' ] );
124
  }
125
 
126
 
@@ -140,7 +140,8 @@ class Products {
140
  return;
141
  }
142
 
143
- ?> <input type="hidden" id="wc_square_variation_manage_stock" name="variable_manage_stock[<?php echo esc_attr( $loop ); ?>]" value="1" /> <?php
 
144
  }
145
 
146
 
@@ -151,10 +152,10 @@ class Products {
151
  */
152
  private function add_product_sync_hooks() {
153
 
154
- add_action( 'woocommerce_update_product', [ $this, 'validate_product_update_and_sync' ] );
155
- add_action( 'trashed_post', [ $this, 'maybe_stage_products_for_deletion' ] );
156
- add_action( 'shutdown', [ $this, 'maybe_sync_staged_products' ] );
157
- add_action( 'shutdown', [ $this, 'maybe_delete_staged_products' ] );
158
  }
159
 
160
 
@@ -176,11 +177,13 @@ class Products {
176
  $label = esc_html__( 'Synced with Square', 'woocommerce-square' );
177
  $selected = isset( $_GET['product_type'] ) && 'synced-with-square' === $_GET['product_type'] ? 'selected=\"selected\"' : '';
178
 
179
- wc_enqueue_js( "
 
180
  jQuery( document ).ready( function( $ ) {
181
- $( 'select#dropdown_product_type' ).append( '<option value=\"synced-with-square\" ' + '" . $selected . "' + '>' + '" . $label ."' + '</option>' );
182
  } );
183
- " );
 
184
  }
185
 
186
 
@@ -194,7 +197,7 @@ class Products {
194
  * @param array $query_vars query variables
195
  * @return array
196
  */
197
- public function filter_products_synced_with_square( $query_vars ){
198
  global $typenow;
199
 
200
  if ( 'product' === $typenow && isset( $_GET['product_type'] ) && 'synced-with-square' === $_GET['product_type'] ) {
@@ -203,16 +206,16 @@ class Products {
203
  unset( $query_vars['product_type'] );
204
 
205
  if ( ! isset( $query_vars['tax_query'] ) ) {
206
- $query_vars['tax_query'] = [];
207
  } else {
208
  $query_vars['tax_query']['relation'] = 'AND';
209
  }
210
 
211
- $query_vars['tax_query'][] = [
212
  'taxonomy' => Product::SYNCED_WITH_SQUARE_TAXONOMY,
213
  'field' => 'slug',
214
- 'terms' => [ 'yes' ],
215
- ];
216
  }
217
 
218
  return $query_vars;
@@ -242,25 +245,27 @@ class Products {
242
  <div class="wc-square-sync-with-square options_group show_if_simple show_if_variable">
243
  <?php
244
 
245
- $selector = '_' . Product::SYNCED_WITH_SQUARE_TAXONOMY;
246
- $value = Product::is_synced_with_square( $product_object ) ? 'yes' : 'no';
247
- $errors = $this->check_product_sync_errors( $product_object );
248
 
249
  $setting_label = wc_square()->get_settings_handler()->is_system_of_record_square() ? __( 'Update product data with Square data', 'woocommerce-square' ) : __( 'Send product data to Square', 'woocommerce-square' );
250
 
251
- woocommerce_wp_checkbox( [
252
- 'id' => $selector,
253
- 'label' => __( 'Sync with Square', 'woocommerce-square' ),
254
- 'value' => $value,
255
- 'cbvalue' => 'yes',
256
- 'default' => 'no',
257
- 'description' => $setting_label,
258
- 'custom_attributes' => ! empty( $errors ) ? [ 'disabled' => 'disabled' ] : [],
259
- ] );
 
 
260
 
261
  ?>
262
  <p class="form-field wc-square-sync-with-square-errors">
263
- <?php foreach ( $this->product_errors as $error_code => $error_message ) : ?>
264
  <?php $styles = ! in_array( $error_code, array_keys( $errors ), true ) ? 'display:none; color:#A00;' : 'display:block; color:#A00;'; ?>
265
  <span class="wc-square-sync-with-square-error <?php echo sanitize_html_class( $error_code ); ?>" style="<?php echo $styles; ?>"><?php echo $this->format_product_error( $error_code, $product_object ); ?></span>
266
  <?php endforeach; ?>
@@ -360,7 +365,13 @@ class Products {
360
 
361
  foreach ( $errors as $error ) {
362
  wc_square()->get_message_handler()->add_error( $error );
363
- Records::set_record( [ 'type' => 'alert', 'product_id' => $product_id, 'message' => $error ] );
 
 
 
 
 
 
364
  }
365
  } else {
366
  $this->maybe_stage_product_for_sync( $product );
@@ -461,13 +472,13 @@ class Products {
461
  $default_value = 'no'; // in individual products context, the value should be always an explicit yes or no
462
  }
463
 
464
- $square_synced = isset( $_REQUEST[ $posted_data_key ] ) && in_array( $_REQUEST[ $posted_data_key ], [ 'yes', 'no' ], true ) ? $_REQUEST[ $posted_data_key ] : $default_value;
465
-
466
  if ( is_string( $square_synced ) ) {
467
  $errors = $this->check_product_sync_errors( $product );
468
  if ( 'no' === $square_synced || empty( $errors ) ) {
469
  Product::set_synced_with_square( $product, $square_synced );
470
- } else if ( ! empty( $errors ) ) {
471
  foreach ( $errors as $error ) {
472
  wc_square()->get_message_handler()->add_error( $error );
473
  }
@@ -497,7 +508,7 @@ class Products {
497
  return;
498
  }
499
 
500
- $errors = [];
501
  $posted_key = '_' . Product::SYNCED_WITH_SQUARE_TAXONOMY;
502
  $set_synced = isset( $_POST[ $posted_key ] ) && 'yes' === $_POST[ $posted_key ];
503
  $was_synced = Product::is_synced_with_square( $product );
@@ -577,7 +588,6 @@ class Products {
577
  } else {
578
  wc_square()->get_api()->remove_inventory( $square_id, $change );
579
  }
580
-
581
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
582
 
583
  wc_square()->log( 'Could not adjust Square inventory for ' . $product->get_formatted_name() . '. ' . $exception->getMessage() );
@@ -608,7 +618,7 @@ class Products {
608
  }
609
 
610
  $duplicated_product->delete_meta_data( Product::SQUARE_ID_META_KEY );
611
- $duplicated_product->delete_meta_data( Product::SQUARE_VARIATION_ID_META_KEY );
612
 
613
  if ( $duplicated_product->is_type( 'variable' ) ) {
614
 
@@ -643,7 +653,7 @@ class Products {
643
  if ( $product && 'hidden' === $product->get_catalog_visibility() ) {
644
 
645
  $product_id = $product->get_id();
646
- $records = Records::get_records( [ 'product' => $product_id ] );
647
 
648
  foreach ( $records as $record ) {
649
 
@@ -655,7 +665,8 @@ class Products {
655
  esc_html__( 'The product catalog visibility has been set to "hidden", as a matching product could not be found in Square on %1$s at %2$s. %3$sCheck sync records%4$s.', 'woocommerce-square' ),
656
  date_i18n( wc_date_format(), $record->get_timestamp() ),
657
  date_i18n( wc_time_format(), $record->get_timestamp() ),
658
- '<a href="' . esc_url( add_query_arg( [ 'section' => 'update' ], wc_square()->get_settings_url() ) ) . '">', '</a>'
 
659
  )
660
  );
661
 
@@ -674,11 +685,11 @@ class Products {
674
  * @return array errors
675
  */
676
  private function check_product_sync_errors( \WC_Product $product ) {
677
- $errors = [];
678
  if ( $product->is_type( 'variable' ) && $product->has_child() ) {
679
  if ( Product::has_multiple_variation_attributes( $product ) ) {
680
  $errors['multiple_attributes'] = $this->format_product_error( 'multiple_attributes', $product );
681
- } else if ( ! Product::has_sku( $product ) ) {
682
  $errors['missing_variation_sku'] = $this->format_product_error( 'missing_variation_sku', $product );
683
  }
684
  } else {
@@ -699,7 +710,7 @@ class Products {
699
  */
700
  private function format_product_error( string $error, \WC_Product $product ) {
701
  return sprintf(
702
- $this->product_errors[$error],
703
  Product::get_product_edit_link( $product )
704
  );
705
  }
23
 
24
  namespace WooCommerce\Square\Handlers;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
42
  private $product_errors;
43
 
44
  /** @var array associative array of memoized errors being output for a product at one time */
45
+ private $output_errors = array();
46
 
47
  /** @var int[] array of product IDs that have been scheduled for sync in this request */
48
+ private $products_to_sync = array();
49
 
50
  /** @var int[] array of product IDs that have been scheduled for deletion in this request */
51
+ private $products_to_delete = array();
52
 
53
  /** @var Square\Plugin plugin instance */
54
  private $plugin;
63
  $this->plugin = $plugin;
64
 
65
  // add common errors
66
+ $this->product_errors = array(
67
  /* translators: Placeholder: %s - product name */
68
  'missing_sku' => __( "Please add an SKU to sync %s with Square. The SKU must match the item's SKU in your Square account.", 'woocommerce-square' ),
69
  /* translators: Placeholder: %s - product name */
70
  'missing_variation_sku' => __( "Please add an SKU to every variation of %s for syncing with Square. Each SKU must match the corresponding item's SKU in your Square account.", 'woocommerce-square' ),
71
  /* translators: Placeholder: %s - product name */
72
  'multiple_attributes' => __( '%s has multiple variation attributes and cannot be synced with Square.', 'woocommerce-square' ),
73
+ );
74
 
75
  // add hooks
76
  $this->add_products_edit_screen_hooks();
89
  private function add_products_edit_screen_hooks() {
90
 
91
  // adds an option to the "Filter by product type" dropdown
92
+ add_action( 'restrict_manage_posts', array( $this, 'add_filter_products_synced_with_square_option' ) );
93
  // allow filtering products by sync status by altering results
94
+ add_filter( 'request', array( $this, 'filter_products_synced_with_square' ) );
95
 
96
  // prevent copying Square data when duplicating a product automatically
97
+ add_action( 'woocommerce_product_duplicate', array( $this, 'handle_product_duplication' ), 20, 2 );
98
 
99
  // handle quick/bulk edit actions in the products edit screen for setting sync status
100
+ add_action( 'woocommerce_product_quick_edit_end', array( $this, 'add_quick_edit_inputs' ) );
101
+ add_action( 'woocommerce_product_bulk_edit_end', array( $this, 'add_bulk_edit_inputs' ) );
102
+ add_action( 'woocommerce_product_quick_edit_save', array( $this, 'set_synced_with_square' ) );
103
+ add_action( 'woocommerce_product_bulk_edit_save', array( $this, 'set_synced_with_square' ) );
104
  }
105
 
106
 
113
  */
114
  private function add_product_edit_screen_hooks() {
115
 
116
+ add_action( 'woocommerce_variation_options', array( $this, 'add_variation_manage_stock' ) );
117
 
118
  // handle individual products input fields for setting sync status
119
+ add_action( 'woocommerce_product_options_general_product_data', array( $this, 'add_product_data_fields' ) );
120
+ add_action( 'woocommerce_admin_process_product_object', array( $this, 'process_product_data' ), 20 );
121
+ add_action( 'woocommerce_before_product_object_save', array( $this, 'maybe_adjust_square_stock' ) );
122
 
123
+ add_action( 'admin_notices', array( $this, 'add_notice_product_hidden_from_catalog' ) );
124
  }
125
 
126
 
140
  return;
141
  }
142
 
143
+ ?> <input type="hidden" id="wc_square_variation_manage_stock" name="variable_manage_stock[<?php echo esc_attr( $loop ); ?>]" value="1" />
144
+ <?php
145
  }
146
 
147
 
152
  */
153
  private function add_product_sync_hooks() {
154
 
155
+ add_action( 'woocommerce_update_product', array( $this, 'validate_product_update_and_sync' ) );
156
+ add_action( 'trashed_post', array( $this, 'maybe_stage_products_for_deletion' ) );
157
+ add_action( 'shutdown', array( $this, 'maybe_sync_staged_products' ) );
158
+ add_action( 'shutdown', array( $this, 'maybe_delete_staged_products' ) );
159
  }
160
 
161
 
177
  $label = esc_html__( 'Synced with Square', 'woocommerce-square' );
178
  $selected = isset( $_GET['product_type'] ) && 'synced-with-square' === $_GET['product_type'] ? 'selected=\"selected\"' : '';
179
 
180
+ wc_enqueue_js(
181
+ "
182
  jQuery( document ).ready( function( $ ) {
183
+ $( 'select#dropdown_product_type' ) . append( '<option value=\"synced-with-square\" ' + '" . $selected . "' + '>' + '" . $label . "' + '</option>' );
184
  } );
185
+ "
186
+ );
187
  }
188
 
189
 
197
  * @param array $query_vars query variables
198
  * @return array
199
  */
200
+ public function filter_products_synced_with_square( $query_vars ) {
201
  global $typenow;
202
 
203
  if ( 'product' === $typenow && isset( $_GET['product_type'] ) && 'synced-with-square' === $_GET['product_type'] ) {
206
  unset( $query_vars['product_type'] );
207
 
208
  if ( ! isset( $query_vars['tax_query'] ) ) {
209
+ $query_vars['tax_query'] = array();
210
  } else {
211
  $query_vars['tax_query']['relation'] = 'AND';
212
  }
213
 
214
+ $query_vars['tax_query'][] = array(
215
  'taxonomy' => Product::SYNCED_WITH_SQUARE_TAXONOMY,
216
  'field' => 'slug',
217
+ 'terms' => array( 'yes' ),
218
+ );
219
  }
220
 
221
  return $query_vars;
245
  <div class="wc-square-sync-with-square options_group show_if_simple show_if_variable">
246
  <?php
247
 
248
+ $selector = '_' . Product::SYNCED_WITH_SQUARE_TAXONOMY;
249
+ $value = Product::is_synced_with_square( $product_object ) ? 'yes' : 'no';
250
+ $errors = $this->check_product_sync_errors( $product_object );
251
 
252
  $setting_label = wc_square()->get_settings_handler()->is_system_of_record_square() ? __( 'Update product data with Square data', 'woocommerce-square' ) : __( 'Send product data to Square', 'woocommerce-square' );
253
 
254
+ woocommerce_wp_checkbox(
255
+ array(
256
+ 'id' => $selector,
257
+ 'label' => __( 'Sync with Square', 'woocommerce-square' ),
258
+ 'value' => $value,
259
+ 'cbvalue' => 'yes',
260
+ 'default' => 'no',
261
+ 'description' => $setting_label,
262
+ 'custom_attributes' => ! empty( $errors ) ? array( 'disabled' => 'disabled' ) : array(),
263
+ )
264
+ );
265
 
266
  ?>
267
  <p class="form-field wc-square-sync-with-square-errors">
268
+ <?php foreach ( $this->product_errors as $error_code => $error_message ) : ?>
269
  <?php $styles = ! in_array( $error_code, array_keys( $errors ), true ) ? 'display:none; color:#A00;' : 'display:block; color:#A00;'; ?>
270
  <span class="wc-square-sync-with-square-error <?php echo sanitize_html_class( $error_code ); ?>" style="<?php echo $styles; ?>"><?php echo $this->format_product_error( $error_code, $product_object ); ?></span>
271
  <?php endforeach; ?>
365
 
366
  foreach ( $errors as $error ) {
367
  wc_square()->get_message_handler()->add_error( $error );
368
+ Records::set_record(
369
+ array(
370
+ 'type' => 'alert',
371
+ 'product_id' => $product_id,
372
+ 'message' => $error,
373
+ )
374
+ );
375
  }
376
  } else {
377
  $this->maybe_stage_product_for_sync( $product );
472
  $default_value = 'no'; // in individual products context, the value should be always an explicit yes or no
473
  }
474
 
475
+ $square_synced = isset( $_REQUEST[ $posted_data_key ] ) && in_array( $_REQUEST[ $posted_data_key ], array( 'yes', 'no' ), true ) ? $_REQUEST[ $posted_data_key ] : $default_value;
476
+
477
  if ( is_string( $square_synced ) ) {
478
  $errors = $this->check_product_sync_errors( $product );
479
  if ( 'no' === $square_synced || empty( $errors ) ) {
480
  Product::set_synced_with_square( $product, $square_synced );
481
+ } elseif ( ! empty( $errors ) ) {
482
  foreach ( $errors as $error ) {
483
  wc_square()->get_message_handler()->add_error( $error );
484
  }
508
  return;
509
  }
510
 
511
+ $errors = array();
512
  $posted_key = '_' . Product::SYNCED_WITH_SQUARE_TAXONOMY;
513
  $set_synced = isset( $_POST[ $posted_key ] ) && 'yes' === $_POST[ $posted_key ];
514
  $was_synced = Product::is_synced_with_square( $product );
588
  } else {
589
  wc_square()->get_api()->remove_inventory( $square_id, $change );
590
  }
 
591
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
592
 
593
  wc_square()->log( 'Could not adjust Square inventory for ' . $product->get_formatted_name() . '. ' . $exception->getMessage() );
618
  }
619
 
620
  $duplicated_product->delete_meta_data( Product::SQUARE_ID_META_KEY );
621
+ $duplicated_product->delete_meta_data( Product::SQUARE_VARIATION_ID_META_KEY );
622
 
623
  if ( $duplicated_product->is_type( 'variable' ) ) {
624
 
653
  if ( $product && 'hidden' === $product->get_catalog_visibility() ) {
654
 
655
  $product_id = $product->get_id();
656
+ $records = Records::get_records( array( 'product' => $product_id ) );
657
 
658
  foreach ( $records as $record ) {
659
 
665
  esc_html__( 'The product catalog visibility has been set to "hidden", as a matching product could not be found in Square on %1$s at %2$s. %3$sCheck sync records%4$s.', 'woocommerce-square' ),
666
  date_i18n( wc_date_format(), $record->get_timestamp() ),
667
  date_i18n( wc_time_format(), $record->get_timestamp() ),
668
+ '<a href="' . esc_url( add_query_arg( array( 'section' => 'update' ), wc_square()->get_settings_url() ) ) . '">',
669
+ '</a>'
670
  )
671
  );
672
 
685
  * @return array errors
686
  */
687
  private function check_product_sync_errors( \WC_Product $product ) {
688
+ $errors = array();
689
  if ( $product->is_type( 'variable' ) && $product->has_child() ) {
690
  if ( Product::has_multiple_variation_attributes( $product ) ) {
691
  $errors['multiple_attributes'] = $this->format_product_error( 'multiple_attributes', $product );
692
+ } elseif ( ! Product::has_sku( $product ) ) {
693
  $errors['missing_variation_sku'] = $this->format_product_error( 'missing_variation_sku', $product );
694
  }
695
  } else {
710
  */
711
  private function format_product_error( string $error, \WC_Product $product ) {
712
  return sprintf(
713
+ $this->product_errors[ $error ],
714
  Product::get_product_edit_link( $product )
715
  );
716
  }
includes/Handlers/Sync.php CHANGED
@@ -28,7 +28,7 @@ use WooCommerce\Square\Plugin;
28
  use WooCommerce\Square\Sync\Interval_Polling;
29
  use WooCommerce\Square\Sync\Records;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * Synchronization handler class
@@ -73,10 +73,10 @@ class Sync {
73
  private function add_hooks() {
74
 
75
  // schedule the interval sync
76
- add_action( 'init', [ $this, 'schedule_sync' ] );
77
 
78
  // run the interval sync when fired by Action Scheduler
79
- add_action( $this->sync_scheduled_event_name, [ $this, 'start_interval_sync' ] );
80
  }
81
 
82
 
@@ -115,8 +115,8 @@ class Sync {
115
  $plugin_id = $this->get_plugin()->get_id();
116
  $interval = $this->get_sync_schedule_interval();
117
 
118
- if ( false === as_next_scheduled_action( $this->sync_scheduled_event_name, [], $plugin_id ) ) {
119
- as_schedule_recurring_action( time() + $interval, $interval, $this->sync_scheduled_event_name, [], $plugin_id );
120
  }
121
  }
122
 
@@ -128,7 +128,7 @@ class Sync {
128
  */
129
  public function unschedule_sync() {
130
 
131
- as_unschedule_action( $this->sync_scheduled_event_name, [], $this->get_plugin()->get_id() );
132
  }
133
 
134
 
@@ -142,9 +142,11 @@ class Sync {
142
  */
143
  public function start_product_import( $dispatch = true ) {
144
 
145
- $job = $this->get_plugin()->get_background_job_handler()->create_job( [
146
- 'action' => 'product_import',
147
- ] );
 
 
148
 
149
  if ( $job ) {
150
 
@@ -166,15 +168,17 @@ class Sync {
166
  * @param int[] $product_ids (optional) array of product IDs to sync
167
  * @return \stdClass|null
168
  */
169
- public function start_manual_sync( $dispatch = true, array $product_ids = [] ) {
170
 
171
  $product_ids = empty( $product_ids ) ? Product::get_products_synced_with_square() : $product_ids;
172
 
173
- $job = $this->get_plugin()->get_background_job_handler()->create_job( [
174
- 'action' => 'sync',
175
- 'manual' => true,
176
- 'product_ids' => $product_ids,
177
- ] );
 
 
178
 
179
  if ( $job ) {
180
 
@@ -198,11 +202,13 @@ class Sync {
198
  */
199
  public function start_manual_deletion( array $product_ids, $dispatch = true ) {
200
 
201
- $job = $this->get_plugin()->get_background_job_handler()->create_job( [
202
- 'action' => 'delete',
203
- 'manual' => true,
204
- 'product_ids' => $product_ids,
205
- ] );
 
 
206
 
207
  if ( $job ) {
208
 
@@ -230,12 +236,14 @@ class Sync {
230
  // use this opportunity to clear old background jobs
231
  $this->get_plugin()->get_background_job_handler()->clear_all_jobs();
232
 
233
- $job = $this->get_plugin()->get_background_job_handler()->create_job( [
234
- 'action' => 'poll',
235
- 'manual' => false,
236
- 'catalog_last_synced_at' => $this->get_last_synced_at(),
237
- 'inventory_last_synced_at' => $this->get_inventory_last_synced_at(),
238
- ] );
 
 
239
 
240
  if ( $job ) {
241
  $this->get_plugin()->get_background_job_handler()->dispatch();
@@ -276,7 +284,7 @@ class Sync {
276
  public function is_sync_in_progress() {
277
 
278
  return ( defined( 'DOING_SQUARE_SYNC' ) && true === DOING_SQUARE_SYNC )
279
- || null !== $this->get_job_in_progress();
280
  }
281
 
282
 
@@ -311,14 +319,16 @@ class Sync {
311
  // only add a record of some products were synced
312
  if ( $products ) {
313
 
314
- Records::set_record( [
315
- 'id' => $job && isset( $job->id ) ? $job->id : null,
316
- 'message' => sprintf(
317
- /* translators: Placeholder: %d number of products processed */
318
- _n( 'Updated data for %d product.', 'Updated data for %d products.', $products, 'woocommerce-square' ),
319
- $products
320
- ),
321
- ] );
 
 
322
  }
323
 
324
  /**
@@ -370,7 +380,7 @@ class Sync {
370
  $job = null;
371
  }
372
 
373
- return $job && isset( $job->status ) && in_array( $job->status, [ 'created', 'queued', 'processing' ], true ) ? $job : null;
374
  }
375
 
376
 
28
  use WooCommerce\Square\Sync\Interval_Polling;
29
  use WooCommerce\Square\Sync\Records;
30
 
31
+ defined( 'ABSPATH' ) || exit;
32
 
33
  /**
34
  * Synchronization handler class
73
  private function add_hooks() {
74
 
75
  // schedule the interval sync
76
+ add_action( 'init', array( $this, 'schedule_sync' ) );
77
 
78
  // run the interval sync when fired by Action Scheduler
79
+ add_action( $this->sync_scheduled_event_name, array( $this, 'start_interval_sync' ) );
80
  }
81
 
82
 
115
  $plugin_id = $this->get_plugin()->get_id();
116
  $interval = $this->get_sync_schedule_interval();
117
 
118
+ if ( false === as_next_scheduled_action( $this->sync_scheduled_event_name, array(), $plugin_id ) ) {
119
+ as_schedule_recurring_action( time() + $interval, $interval, $this->sync_scheduled_event_name, array(), $plugin_id );
120
  }
121
  }
122
 
128
  */
129
  public function unschedule_sync() {
130
 
131
+ as_unschedule_action( $this->sync_scheduled_event_name, array(), $this->get_plugin()->get_id() );
132
  }
133
 
134
 
142
  */
143
  public function start_product_import( $dispatch = true ) {
144
 
145
+ $job = $this->get_plugin()->get_background_job_handler()->create_job(
146
+ array(
147
+ 'action' => 'product_import',
148
+ )
149
+ );
150
 
151
  if ( $job ) {
152
 
168
  * @param int[] $product_ids (optional) array of product IDs to sync
169
  * @return \stdClass|null
170
  */
171
+ public function start_manual_sync( $dispatch = true, array $product_ids = array() ) {
172
 
173
  $product_ids = empty( $product_ids ) ? Product::get_products_synced_with_square() : $product_ids;
174
 
175
+ $job = $this->get_plugin()->get_background_job_handler()->create_job(
176
+ array(
177
+ 'action' => 'sync',
178
+ 'manual' => true,
179
+ 'product_ids' => $product_ids,
180
+ )
181
+ );
182
 
183
  if ( $job ) {
184
 
202
  */
203
  public function start_manual_deletion( array $product_ids, $dispatch = true ) {
204
 
205
+ $job = $this->get_plugin()->get_background_job_handler()->create_job(
206
+ array(
207
+ 'action' => 'delete',
208
+ 'manual' => true,
209
+ 'product_ids' => $product_ids,
210
+ )
211
+ );
212
 
213
  if ( $job ) {
214
 
236
  // use this opportunity to clear old background jobs
237
  $this->get_plugin()->get_background_job_handler()->clear_all_jobs();
238
 
239
+ $job = $this->get_plugin()->get_background_job_handler()->create_job(
240
+ array(
241
+ 'action' => 'poll',
242
+ 'manual' => false,
243
+ 'catalog_last_synced_at' => $this->get_last_synced_at(),
244
+ 'inventory_last_synced_at' => $this->get_inventory_last_synced_at(),
245
+ )
246
+ );
247
 
248
  if ( $job ) {
249
  $this->get_plugin()->get_background_job_handler()->dispatch();
284
  public function is_sync_in_progress() {
285
 
286
  return ( defined( 'DOING_SQUARE_SYNC' ) && true === DOING_SQUARE_SYNC )
287
+ || null !== $this->get_job_in_progress();
288
  }
289
 
290
 
319
  // only add a record of some products were synced
320
  if ( $products ) {
321
 
322
+ Records::set_record(
323
+ array(
324
+ 'id' => $job && isset( $job->id ) ? $job->id : null,
325
+ 'message' => sprintf(
326
+ /* translators: Placeholder: %d number of products processed */
327
+ _n( 'Updated data for %d product.', 'Updated data for %d products.', $products, 'woocommerce-square' ),
328
+ $products
329
+ ),
330
+ )
331
+ );
332
  }
333
 
334
  /**
380
  $job = null;
381
  }
382
 
383
+ return $job && isset( $job->status ) && in_array( $job->status, array( 'created', 'queued', 'processing' ), true ) ? $job : null;
384
  }
385
 
386
 
includes/Lifecycle.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
@@ -43,18 +43,19 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
43
  *
44
  * @since 2.0.0
45
  *
46
- * @param Plugin $plugin main instance
47
  */
48
  public function __construct( Plugin $plugin ) {
49
 
50
  parent::__construct( $plugin );
51
 
52
- // plugin upgrade path: maps automatically each semver to upgrade_to_x_y_z() protected method
53
- $this->upgrade_versions = [
54
  '2.0.0',
55
  '2.0.4',
56
  '2.1.5',
57
- ];
 
58
  }
59
 
60
 
@@ -65,7 +66,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
65
  */
66
  protected function install() {
67
 
68
- // create the db table for the customer index
69
  Gateway\Customer_Helper::create_table();
70
 
71
  /**
@@ -84,7 +85,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
84
  *
85
  * @since 2.0.0
86
  *
87
- * @param string $installed_version semver
88
  */
89
  protected function upgrade( $installed_version ) {
90
 
@@ -109,16 +110,10 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
109
  */
110
  protected function upgrade_to_2_0_0() {
111
 
112
- // create the db table for the customer index
113
  Gateway\Customer_Helper::create_table();
114
 
115
- /** @see \wc_set_time_limit() */
116
- if ( function_exists( 'set_time_limit' )
117
- && ! ini_get( 'safe_mode' )
118
- && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) ) {
119
-
120
- @set_time_limit( 300 );
121
- }
122
 
123
  // migrate all the things!
124
  $syncing_products = $this->migrate_plugin_settings();
@@ -126,19 +121,19 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
126
  $this->migrate_gateway_settings();
127
  $this->migrate_orders();
128
 
129
- // only set the products "sync" status if v2 is now configured to sync products
130
  if ( $syncing_products ) {
131
 
132
  $this->migrate_products();
133
 
134
- // assume a last sync occurred before upgrading
135
  $this->get_plugin()->get_sync_handler()->set_last_synced_at();
136
  $this->get_plugin()->get_sync_handler()->set_inventory_last_synced_at();
137
  }
138
 
139
  $this->migrate_customers();
140
 
141
- // mark upgrade complete
142
  update_option( 'wc_square_updated_to_2_0_0', true );
143
  }
144
 
@@ -150,8 +145,8 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
150
  */
151
  protected function upgrade_to_2_0_4() {
152
 
153
- $v1_settings = get_option( 'woocommerce_squareconnect_settings', [] );
154
- $v2_settings = get_option( 'wc_square_settings', [] );
155
 
156
  $v2_settings = $this->get_migrated_system_of_record( $v1_settings, $v2_settings );
157
 
@@ -170,6 +165,58 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
170
  Gateway\Customer_Helper::create_table();
171
  }
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  /**
174
  * Migrates plugin settings from v1 to v2.
175
  *
@@ -183,20 +230,21 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
183
 
184
  $this->get_plugin()->log( 'Migrating plugin settings...' );
185
 
186
- // get legacy and new default settings
187
- $new_settings = get_option( 'wc_square_settings', [] );
188
- $legacy_settings = get_option( 'woocommerce_squareconnect_settings', [] );
189
- $email_settings = get_option( 'woocommerce_wc_square_sync_completed_settings', [] );
190
 
191
- // bail if they already have v2 settings present
192
  if ( ! empty( $new_settings ) ) {
193
  return;
194
  }
195
 
196
- // handle access token first
197
- if ( $legacy_access_token = get_option( 'woocommerce_square_merchant_access_token' ) ) {
 
198
 
199
- // the access token was previously stored unencrypted
200
  if ( ! empty( $legacy_access_token ) && Utilities\Encryption_Utility::is_encryption_supported() ) {
201
 
202
  $encryption = new Utilities\Encryption_Utility();
@@ -204,46 +252,46 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
204
  try {
205
  $legacy_access_token = $encryption->encrypt_data( $legacy_access_token );
206
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
207
- // log the event, but don't halt the process
208
  $this->get_plugin()->log( 'Could not encrypt access token during upgrade. ' . $exception->getMessage() );
209
  }
210
  }
211
 
212
- // previously only 'production' environment was assumed
213
- $access_tokens = get_option( 'wc_square_access_tokens', [] );
214
  $access_tokens['production'] = is_string( $legacy_access_token ) ? $legacy_access_token : '';
215
 
216
  update_option( 'wc_square_access_tokens', $access_tokens );
217
  }
218
 
219
- // migrate store location
220
  if ( ! empty( $legacy_settings['location'] ) ) {
221
  $new_settings['location_id'] = $legacy_settings['location'];
222
  }
223
 
224
- // toggle debug logging
225
- $new_settings['debug_logging_enabled'] = isset( $legacy_settings['logging'] ) && in_array( $legacy_settings['logging'], [ 'yes', 'no' ], true ) ? $legacy_settings['logging'] : 'no';
226
 
227
- // set the SOR and inventory sync values
228
  $new_settings = $this->get_migrated_system_of_record( $legacy_settings, $new_settings );
229
 
230
- // migrate email notification settings: if there's a recipient, we enable email and pass recipient(s) to email setting
231
  if ( isset( $legacy_settings['sync_email'] ) && is_string( $legacy_settings['sync_email'] ) && '' !== trim( $legacy_settings['sync_email'] ) ) {
232
  $email_settings['enabled'] = 'yes';
233
- $email_settings['recipient'] = $legacy_settings['sync_email'] ;
234
  } else {
235
  $email_settings['enabled'] = 'no';
236
  $email_settings['recipient'] = '';
237
  }
238
 
239
- // save email settings
240
  update_option( 'woocommerce_wc_square_sync_completed_settings', $email_settings );
241
- // save plugin settings
242
  update_option( 'wc_square_settings', $new_settings );
243
 
244
  $this->get_plugin()->log( 'Plugin settings migration complete.' );
245
 
246
- return isset( $new_settings['system_of_record'] ) && $new_settings['system_of_record'] !== Settings::SYSTEM_OF_RECORD_DISABLED;
247
  }
248
 
249
 
@@ -258,10 +306,10 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
258
 
259
  $this->get_plugin()->log( 'Migrating gateway settings...' );
260
 
261
- $legacy_settings = get_option( 'woocommerce_square_settings', [] );
262
- $new_settings = get_option( 'woocommerce_square_credit_card_settings', [] );
263
 
264
- // bail if they already have v2 settings present
265
  if ( ! empty( $new_settings ) ) {
266
  return;
267
  }
@@ -272,18 +320,18 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
272
 
273
  if ( isset( $legacy_settings['title'] ) && is_string( $legacy_settings['title'] ) ) {
274
  $new_settings['title'] = $legacy_settings['title'];
275
- }
276
 
277
  if ( isset( $legacy_settings['description'] ) && is_string( $legacy_settings['description'] ) ) {
278
  $new_settings['description'] = $legacy_settings['description'];
279
  }
280
 
281
- // note: the following is not an error, the setting on v1 intends "delayed" capture, hence authorization only, if set
282
  if ( isset( $legacy_settings['capture'] ) ) {
283
  $new_settings['transaction_type'] = 'yes' === $legacy_settings['capture'] ? Gateway::TRANSACTION_TYPE_AUTHORIZATION : Gateway::TRANSACTION_TYPE_CHARGE;
284
  }
285
 
286
- // not quite the same, since tokenization is a new thing, but we could presume the intention to let customers save their payment details
287
  if ( isset( $legacy_settings['create_customer'] ) ) {
288
  $new_settings['tokenization'] = 'yes' === $legacy_settings['create_customer'] ? 'yes' : 'no';
289
  }
@@ -292,16 +340,16 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
292
  $new_settings['debug_mode'] = 'yes' === $legacy_settings['logging'] ? 'log' : 'off';
293
  }
294
 
295
- // there was no card types setting in v1
296
- $new_settings['card_types'] = [
297
  'VISA',
298
  'MC',
299
  'AMEX',
300
  'JCB',
301
- // purposefully omit dinersclub & discover
302
- ];
303
 
304
- // save migrated settings
305
  update_option( 'woocommerce_square_credit_card_settings', $new_settings );
306
 
307
  $this->get_plugin()->log( 'Gateway settings migration complete.' );
@@ -320,12 +368,20 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
320
 
321
  $this->get_plugin()->log( 'Migrating orders data...' );
322
 
323
- // move charge captured flag in orders to SkyVerge framework meta key
324
- $wpdb->update( $wpdb->postmeta, [ 'meta_key' => '_wc_square_credit_card_charge_captured' ], [ 'meta_key' => '_square_charge_captured' ] );
325
-
326
- // move payment ID to new gateway ID meta key value
327
- $wpdb->update( $wpdb->postmeta, [ 'meta_value' => 'square_credit_card' ], [ 'meta_key' => '_payment_method', 'meta_value' => 'square' ] );
328
-
 
 
 
 
 
 
 
 
329
  $this->get_plugin()->log( 'Orders migration complete.' );
330
  }
331
 
@@ -342,42 +398,45 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
342
 
343
  $this->get_plugin()->log( 'Migrating products data...' );
344
 
345
- // the handling in v1 was reversed, so we want products where sync wasn't disabled
346
- $legacy_product_ids = get_posts( [
347
- 'nopaging' => true,
348
- 'post_type' => 'product',
349
- 'post_status' => 'all',
350
- 'fields' => 'ids',
351
- 'meta_query' => [
352
- 'relation' => 'OR',
353
- [
354
- 'key' => '_wcsquare_disable_sync',
355
- 'value' => 'no',
356
- ],
357
- [
358
- 'key' => '_wcsquare_disable_sync',
359
- 'compare' => 'NOT EXISTS',
360
- ]
361
- ],
362
- ] );
363
-
364
- // in v2 we turn those products as flagged to be sync-enabled instead
 
 
 
365
  if ( ! empty( $legacy_product_ids ) ) {
366
 
367
- $failed_products = [];
368
 
369
- // ensure taxonomy is registered at this stage
370
  if ( ! taxonomy_exists( Product::SYNCED_WITH_SQUARE_TAXONOMY ) ) {
371
  Product::init_taxonomies();
372
  }
373
 
374
- // will not create the term if already exists
375
  wp_create_term( 'yes', Product::SYNCED_WITH_SQUARE_TAXONOMY );
376
 
377
- // set Square sync status via taxonomy term
378
  foreach ( $legacy_product_ids as $i => $product_id ) {
379
 
380
- $set_term = wp_set_object_terms( $product_id, [ 'yes' ], Product::SYNCED_WITH_SQUARE_TAXONOMY );
381
 
382
  if ( ! is_array( $set_term ) ) {
383
 
@@ -387,7 +446,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
387
  }
388
  }
389
 
390
- // log any errors
391
  if ( ! empty( $failed_products ) ) {
392
  $this->get_plugin()->log( 'Could not update sync with Square status for products with ID: ' . implode( ', ', array_unique( $failed_products ) ) . '.' );
393
  }
@@ -407,7 +466,7 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
407
 
408
  $this->get_plugin()->log( 'Migrating customer data.' );
409
 
410
- $rows = (int) $wpdb->update( $wpdb->usermeta, [ 'meta_key' => 'wc_square_customer_id' ], [ 'meta_key' => '_square_customer_id' ] );
411
 
412
  $this->get_plugin()->log( sprintf( '%d customers migrated', $rows ) );
413
  }
@@ -418,8 +477,8 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
418
  *
419
  * @since 2.0.2
420
  *
421
- * @param array $v1_settings v1 plugin settings
422
- * @param array $v2_settings v2 plugin settings
423
  * @return array
424
  */
425
  private function get_migrated_system_of_record( $v1_settings, $v2_settings ) {
@@ -434,5 +493,23 @@ class Lifecycle extends Framework\Plugin\Lifecycle {
434
  return $v2_settings;
435
  }
436
 
 
 
 
 
 
 
 
 
 
 
 
437
 
 
 
 
 
 
 
 
438
  }
23
 
24
  namespace WooCommerce\Square;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Product;
43
  *
44
  * @since 2.0.0
45
  *
46
+ * @param Plugin $plugin main instance.
47
  */
48
  public function __construct( Plugin $plugin ) {
49
 
50
  parent::__construct( $plugin );
51
 
52
+ // plugin upgrade path: maps automatically each semver to upgrade_to_x_y_z() protected method.
53
+ $this->upgrade_versions = array(
54
  '2.0.0',
55
  '2.0.4',
56
  '2.1.5',
57
+ '2.2.0',
58
+ );
59
  }
60
 
61
 
66
  */
67
  protected function install() {
68
 
69
+ // create the db table for the customer index.
70
  Gateway\Customer_Helper::create_table();
71
 
72
  /**
85
  *
86
  * @since 2.0.0
87
  *
88
+ * @param string $installed_version semver.
89
  */
90
  protected function upgrade( $installed_version ) {
91
 
110
  */
111
  protected function upgrade_to_2_0_0() {
112
 
113
+ // create the db table for the customer index.
114
  Gateway\Customer_Helper::create_table();
115
 
116
+ wc_set_time_limit( 300 );
 
 
 
 
 
 
117
 
118
  // migrate all the things!
119
  $syncing_products = $this->migrate_plugin_settings();
121
  $this->migrate_gateway_settings();
122
  $this->migrate_orders();
123
 
124
+ // only set the products "sync" status if v2 is now configured to sync products.
125
  if ( $syncing_products ) {
126
 
127
  $this->migrate_products();
128
 
129
+ // assume a last sync occurred before upgrading.
130
  $this->get_plugin()->get_sync_handler()->set_last_synced_at();
131
  $this->get_plugin()->get_sync_handler()->set_inventory_last_synced_at();
132
  }
133
 
134
  $this->migrate_customers();
135
 
136
+ // mark upgrade complete.
137
  update_option( 'wc_square_updated_to_2_0_0', true );
138
  }
139
 
145
  */
146
  protected function upgrade_to_2_0_4() {
147
 
148
+ $v1_settings = get_option( 'woocommerce_squareconnect_settings', array() );
149
+ $v2_settings = get_option( 'wc_square_settings', array() );
150
 
151
  $v2_settings = $this->get_migrated_system_of_record( $v1_settings, $v2_settings );
152
 
165
  Gateway\Customer_Helper::create_table();
166
  }
167
 
168
+ /**
169
+ * Generates a milestone notice message.
170
+ *
171
+ * @since 2.1.7
172
+ *
173
+ * @param string $custom_message Custom text that notes what milestone was completed.
174
+ * @return string
175
+ */
176
+ protected function generate_milestone_notice_message( $custom_message ) {
177
+
178
+ $message = '';
179
+
180
+ if ( $this->get_plugin()->get_reviews_url() ) {
181
+
182
+ // to be prepended at random to each milestone notice.
183
+ $exclamations = array(
184
+ __( 'Awesome', 'woocommerce-square' ),
185
+ __( 'Congratulations', 'woocommerce-square' ),
186
+ __( 'Great', 'woocommerce-square' ),
187
+ __( 'Fantastic', 'woocommerce-square' ),
188
+ );
189
+
190
+ $message = $exclamations[ array_rand( $exclamations ) ] . ', ' . esc_html( $custom_message ) . ' ';
191
+
192
+ $message .= sprintf(
193
+ /* translators: Placeholders: %1$s - plugin name, %2$s - <a> tag, %3$s - </a> tag, %4$s - <a> tag, %5$s - </a> tag */
194
+ __( 'Are you having a great experience with %1$s so far? Please consider %2$sleaving a review%3$s! If things aren\'t going quite as expected, we\'re happy to help -- please %4$sreach out to our support team%5$s.', 'woocommerce-square' ),
195
+ '<strong>' . esc_html( $this->get_plugin()->get_plugin_name() ) . '</strong>',
196
+ '<a href="' . esc_url( $this->get_plugin()->get_reviews_url() ) . '">',
197
+ '</a>',
198
+ '<a href="' . esc_url( $this->get_plugin()->get_support_url() ) . '">',
199
+ '</a>'
200
+ );
201
+ }
202
+
203
+ return $message;
204
+ }
205
+
206
+ /**
207
+ * Upgrades to version 2.2.0.
208
+ *
209
+ * @since 2.2.0
210
+ */
211
+ protected function upgrade_to_2_2_0() {
212
+
213
+ $v1_settings = get_option( 'wc_square_settings', array() );
214
+
215
+ $v2_settings = $this->set_environment_location_id( $v1_settings );
216
+
217
+ update_option( 'wc_square_settings', $v2_settings );
218
+ }
219
+
220
  /**
221
  * Migrates plugin settings from v1 to v2.
222
  *
230
 
231
  $this->get_plugin()->log( 'Migrating plugin settings...' );
232
 
233
+ // get legacy and new default settings.
234
+ $new_settings = get_option( 'wc_square_settings', array() );
235
+ $legacy_settings = get_option( 'woocommerce_squareconnect_settings', array() );
236
+ $email_settings = get_option( 'woocommerce_wc_square_sync_completed_settings', array() );
237
 
238
+ // bail if they already have v2 settings present.
239
  if ( ! empty( $new_settings ) ) {
240
  return;
241
  }
242
 
243
+ // handle access token first.
244
+ $legacy_access_token = get_option( 'woocommerce_square_merchant_access_token' );
245
+ if ( $legacy_access_token ) {
246
 
247
+ // the access token was previously stored unencrypted.
248
  if ( ! empty( $legacy_access_token ) && Utilities\Encryption_Utility::is_encryption_supported() ) {
249
 
250
  $encryption = new Utilities\Encryption_Utility();
252
  try {
253
  $legacy_access_token = $encryption->encrypt_data( $legacy_access_token );
254
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
255
+ // log the event, but don't halt the process.
256
  $this->get_plugin()->log( 'Could not encrypt access token during upgrade. ' . $exception->getMessage() );
257
  }
258
  }
259
 
260
+ // previously only 'production' environment was assumed.
261
+ $access_tokens = get_option( 'wc_square_access_tokens', array() );
262
  $access_tokens['production'] = is_string( $legacy_access_token ) ? $legacy_access_token : '';
263
 
264
  update_option( 'wc_square_access_tokens', $access_tokens );
265
  }
266
 
267
+ // migrate store location.
268
  if ( ! empty( $legacy_settings['location'] ) ) {
269
  $new_settings['location_id'] = $legacy_settings['location'];
270
  }
271
 
272
+ // toggle debug logging.
273
+ $new_settings['debug_logging_enabled'] = isset( $legacy_settings['logging'] ) && in_array( $legacy_settings['logging'], array( 'yes', 'no' ), true ) ? $legacy_settings['logging'] : 'no';
274
 
275
+ // set the SOR and inventory sync values.
276
  $new_settings = $this->get_migrated_system_of_record( $legacy_settings, $new_settings );
277
 
278
+ // migrate email notification settings: if there's a recipient, we enable email and pass recipient(s) to email setting.
279
  if ( isset( $legacy_settings['sync_email'] ) && is_string( $legacy_settings['sync_email'] ) && '' !== trim( $legacy_settings['sync_email'] ) ) {
280
  $email_settings['enabled'] = 'yes';
281
+ $email_settings['recipient'] = $legacy_settings['sync_email'];
282
  } else {
283
  $email_settings['enabled'] = 'no';
284
  $email_settings['recipient'] = '';
285
  }
286
 
287
+ // save email settings.
288
  update_option( 'woocommerce_wc_square_sync_completed_settings', $email_settings );
289
+ // save plugin settings.
290
  update_option( 'wc_square_settings', $new_settings );
291
 
292
  $this->get_plugin()->log( 'Plugin settings migration complete.' );
293
 
294
+ return isset( $new_settings['system_of_record'] ) && Settings::SYSTEM_OF_RECORD_DISABLED !== $new_settings['system_of_record'];
295
  }
296
 
297
 
306
 
307
  $this->get_plugin()->log( 'Migrating gateway settings...' );
308
 
309
+ $legacy_settings = get_option( 'woocommerce_square_settings', array() );
310
+ $new_settings = get_option( 'woocommerce_square_credit_card_settings', array() );
311
 
312
+ // bail if they already have v2 settings present.
313
  if ( ! empty( $new_settings ) ) {
314
  return;
315
  }
320
 
321
  if ( isset( $legacy_settings['title'] ) && is_string( $legacy_settings['title'] ) ) {
322
  $new_settings['title'] = $legacy_settings['title'];
323
+ }
324
 
325
  if ( isset( $legacy_settings['description'] ) && is_string( $legacy_settings['description'] ) ) {
326
  $new_settings['description'] = $legacy_settings['description'];
327
  }
328
 
329
+ // note: the following is not an error, the setting on v1 intends "delayed" capture, hence authorization only, if set.
330
  if ( isset( $legacy_settings['capture'] ) ) {
331
  $new_settings['transaction_type'] = 'yes' === $legacy_settings['capture'] ? Gateway::TRANSACTION_TYPE_AUTHORIZATION : Gateway::TRANSACTION_TYPE_CHARGE;
332
  }
333
 
334
+ // not quite the same, since tokenization is a new thing, but we could presume the intention to let customers save their payment details.
335
  if ( isset( $legacy_settings['create_customer'] ) ) {
336
  $new_settings['tokenization'] = 'yes' === $legacy_settings['create_customer'] ? 'yes' : 'no';
337
  }
340
  $new_settings['debug_mode'] = 'yes' === $legacy_settings['logging'] ? 'log' : 'off';
341
  }
342
 
343
+ // there was no card types setting in v1.
344
+ $new_settings['card_types'] = array(
345
  'VISA',
346
  'MC',
347
  'AMEX',
348
  'JCB',
349
+ // purposefully omit dinersclub & discover.
350
+ );
351
 
352
+ // save migrated settings.
353
  update_option( 'woocommerce_square_credit_card_settings', $new_settings );
354
 
355
  $this->get_plugin()->log( 'Gateway settings migration complete.' );
368
 
369
  $this->get_plugin()->log( 'Migrating orders data...' );
370
 
371
+ // move charge captured flag in orders to SkyVerge framework meta key.
372
+ $wpdb->update( $wpdb->postmeta, array( 'meta_key' => '_wc_square_credit_card_charge_captured' ), array( 'meta_key' => '_square_charge_captured' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
373
+
374
+ // move payment ID to new gateway ID meta key value.
375
+ $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
376
+ $wpdb->postmeta,
377
+ array(
378
+ 'meta_value' => 'square_credit_card', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
379
+ ),
380
+ array(
381
+ 'meta_key' => '_payment_method', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
382
+ 'meta_value' => 'square', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
383
+ )
384
+ );
385
  $this->get_plugin()->log( 'Orders migration complete.' );
386
  }
387
 
398
 
399
  $this->get_plugin()->log( 'Migrating products data...' );
400
 
401
+ // the handling in v1 was reversed, so we want products where sync wasn't disabled.
402
+ $legacy_product_ids = get_posts(
403
+ array(
404
+ 'nopaging' => true,
405
+ 'post_type' => 'product',
406
+ 'post_status' => 'all',
407
+ 'fields' => 'ids',
408
+ 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
409
+
410
+ 'relation' => 'OR',
411
+ array(
412
+ 'key' => '_wcsquare_disable_sync',
413
+ 'value' => 'no',
414
+ ),
415
+ array(
416
+ 'key' => '_wcsquare_disable_sync',
417
+ 'compare' => 'NOT EXISTS',
418
+ ),
419
+ ),
420
+ )
421
+ );
422
+
423
+ // in v2 we turn those products as flagged to be sync-enabled instead.
424
  if ( ! empty( $legacy_product_ids ) ) {
425
 
426
+ $failed_products = array();
427
 
428
+ // ensure taxonomy is registered at this stage.
429
  if ( ! taxonomy_exists( Product::SYNCED_WITH_SQUARE_TAXONOMY ) ) {
430
  Product::init_taxonomies();
431
  }
432
 
433
+ // will not create the term if already exists.
434
  wp_create_term( 'yes', Product::SYNCED_WITH_SQUARE_TAXONOMY );
435
 
436
+ // set Square sync status via taxonomy term.
437
  foreach ( $legacy_product_ids as $i => $product_id ) {
438
 
439
+ $set_term = wp_set_object_terms( $product_id, array( 'yes' ), Product::SYNCED_WITH_SQUARE_TAXONOMY );
440
 
441
  if ( ! is_array( $set_term ) ) {
442
 
446
  }
447
  }
448
 
449
+ // log any errors.
450
  if ( ! empty( $failed_products ) ) {
451
  $this->get_plugin()->log( 'Could not update sync with Square status for products with ID: ' . implode( ', ', array_unique( $failed_products ) ) . '.' );
452
  }
466
 
467
  $this->get_plugin()->log( 'Migrating customer data.' );
468
 
469
+ $rows = (int) $wpdb->update( $wpdb->usermeta, array( 'meta_key' => 'wc_square_customer_id' ), array( 'meta_key' => '_square_customer_id' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
470
 
471
  $this->get_plugin()->log( sprintf( '%d customers migrated', $rows ) );
472
  }
477
  *
478
  * @since 2.0.2
479
  *
480
+ * @param array $v1_settings v1 plugin settings.
481
+ * @param array $v2_settings v2 plugin settings.
482
  * @return array
483
  */
484
  private function get_migrated_system_of_record( $v1_settings, $v2_settings ) {
493
  return $v2_settings;
494
  }
495
 
496
+ /**
497
+ * Adds environment specific location_id to, and removes general location_id from v1 setting array.
498
+ *
499
+ * @since 2.2.0
500
+ *
501
+ * @param array $v1_settings v1 plugin settings.
502
+ * @return array
503
+ */
504
+ private function set_environment_location_id( $v1_settings ) {
505
+
506
+ $environment = isset( $v1_settings['enable_sandbox'] ) && 'yes' === $v1_settings['enable_sandbox'] ? 'sandbox' : 'production';
507
 
508
+ if ( ! isset( $v1_settings[ $environment . '_location_id' ] ) ) {
509
+ $v1_location_id = isset( $v1_settings['location_id'] ) ? $v1_settings['location_id'] : '';
510
+ $v1_settings[ $environment . '_location_id' ] = $v1_location_id;
511
+ }
512
+
513
+ return $v1_settings;
514
+ }
515
  }
includes/Plugin.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Background_Job;
@@ -42,7 +42,7 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
42
 
43
 
44
  /** plugin version number */
45
- const VERSION = '2.1.6';
46
 
47
  /** plugin ID */
48
  const PLUGIN_ID = 'square';
@@ -91,19 +91,19 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
91
  parent::__construct(
92
  self::PLUGIN_ID,
93
  self::VERSION,
94
- [
95
- 'text_domain' => 'woocommerce-square',
96
- 'gateways' => [ self::GATEWAY_ID => Gateway::class ],
97
- 'require_ssl' => true,
98
- 'supports' => [
99
  self::FEATURE_CAPTURE_CHARGE,
100
  self::FEATURE_CUSTOMER_ID,
101
  self::FEATURE_MY_PAYMENT_METHODS,
102
- ],
103
- 'dependencies' => [
104
- 'php_extensions' => [ 'curl', 'json', 'mbstring' ],
105
- ],
106
- ]
107
  );
108
 
109
  $this->includes();
@@ -115,10 +115,10 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
115
  */
116
  do_action( 'wc_square_loaded' );
117
 
118
- add_action( 'woocommerce_register_taxonomy', [ $this, 'init_taxonomies' ] );
119
 
120
- add_filter( 'woocommerce_locate_template', [ $this, 'locate_template' ], 20, 3 );
121
- add_filter( 'woocommerce_locate_core_template', [ $this, 'locate_template' ], 20, 3 );
122
  }
123
 
124
 
@@ -158,7 +158,7 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
158
  public function add_api_request_logging() {
159
 
160
  if ( ! has_action( 'wc_' . $this->get_id() . '_api_request_performed' ) ) {
161
- add_action( 'wc_' . $this->get_id() . '_api_request_performed', [ $this, 'log_api_request' ], 10, 2 );
162
  }
163
  }
164
 
@@ -273,13 +273,18 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
273
 
274
  $message = sprintf(
275
  __( 'Heads up! There may be a problem with your connection to Square. In order to continue accepting payments, please %1$sdisconnect and re-connect your site%2$s.', 'woocommerce-square' ),
276
- '<a href="' . esc_url( $this->get_settings_url() ) . '">', '</a>'
 
277
  );
278
 
279
- $this->get_admin_notice_handler()->add_admin_notice( $message, 'refresh-failed', [
280
- 'dismissible' => false,
281
- 'notice_class' => 'notice-warning',
282
- ] );
 
 
 
 
283
  }
284
 
285
  if ( $this->get_settings_handler()->is_connected() ) {
@@ -298,7 +303,8 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
298
  $instruction = sprintf(
299
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
300
  __( 'Visit the %1$splugin settings%2$s to set your business location.', 'woocommerce-square' ),
301
- '<a href="' . esc_url( $this->get_settings_url() ) . '">', '</a>'
 
302
  );
303
  }
304
 
@@ -313,8 +319,11 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
313
  $instruction = sprintf(
314
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - product count, %3$s - </strong> tag, %4$s - <a> tag, %5$s - </a> tag */
315
  __( '%1$s%2$d products%3$s are marked "sync with Square". %4$sStart a new sync now &raquo;%5$s', 'woocommerce-square' ),
316
- '<strong>', count( Product::get_products_synced_with_square() ), '</strong>',
317
- '<a href="' . esc_url( add_query_arg( 'section', 'update', $this->get_settings_url() ) ) . '">', '</a>'
 
 
 
318
  );
319
 
320
  } else {
@@ -322,8 +331,10 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
322
  $instruction = sprintf(
323
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
324
  __( '%1$sNo products%2$s are marked "sync with Square". %3$sUpdate your products to sync data &raquo;%4$s', 'woocommerce-square' ),
325
- '<strong>', '</strong>',
326
- '<a href="' . esc_url( admin_url( 'edit.php?post_type=product' ) ) . '">', '</a>'
 
 
327
  );
328
  }
329
 
@@ -335,14 +346,18 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
335
 
336
  $message = sprintf(
337
  __( 'Heads up! Square is configured to sync product inventory, but WooCommerce stock management is disabled. Please %1$senable stock management%2$s to ensure product inventory counts are kept in sync.', 'woocommerce-square' ),
338
- '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=products&section=inventory' ) ) . '">', '</a>'
 
339
  );
340
 
341
- $this->get_admin_notice_handler()->add_admin_notice( $message, 'enable-wc-sync', [
342
- 'notice_class' => 'notice-warning',
343
- ] );
 
 
 
 
344
  }
345
-
346
  } else {
347
 
348
  if ( $this->is_plugin_settings() ) {
@@ -354,7 +369,8 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
354
  $instruction = sprintf(
355
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
356
  __( 'To get started, %1$sconnect with Square &raquo;%2$s', 'woocommerce-square' ),
357
- '<a href="' . esc_url( $this->get_settings_url() ) . '">', '</a>'
 
358
  );
359
  }
360
 
@@ -387,11 +403,13 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
387
  esc_html__( '%1$s has been updated to version %2$s. In order to continue syncing product inventory, please make sure to disconnect and reconnect with Square from the %3$splugin settings%4$s and re-sync your products. Read more in the %5$supdated documentation%6$s.', 'woocommerce-square' ),
388
  '<strong>' . esc_html( $this->get_plugin_name() ) . '</strong>',
389
  $this->get_version(),
390
- '<a href="' . esc_url( $this->get_settings_url() ) . '">', '</a>',
391
- '<a href="' . esc_url( $this->get_documentation_url() ) . '">', '</a>'
 
 
392
  ),
393
  'updated-to-v2',
394
- [ 'notice_class' => 'notice-warning' ]
395
  );
396
  }
397
  }
@@ -404,27 +422,32 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
404
  */
405
  protected function add_base_location_admin_notice() {
406
 
407
- $accepted_countries = [
408
  'US',
409
  'CA',
410
  'GB',
411
  'AU',
412
  'JP',
413
- ];
414
 
415
  $base_location = wc_get_base_location();
416
 
417
  if ( isset( $base_location['country'] ) && ! in_array( $base_location['country'], $accepted_countries, true ) ) {
418
 
419
- $this->get_admin_notice_handler()->add_admin_notice( sprintf(
420
- /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - 2-character country code, %4$s - comma separated list of 2-character country codes */
421
- __( '%1$sWooCommerce Square:%2$s Your base country is %3$s, but Square can’t accept transactions from merchants outside of %4$s.', 'woocommerce-square' ),
422
- '<strong>', '</strong>',
423
- esc_html( $base_location['country'] ),
424
- esc_html( Framework\SV_WC_Helper::list_array_items( $accepted_countries ) )
425
- ), 'wc-square-base-location', [
426
- 'notice_class' => 'notice-error',
427
- ] );
 
 
 
 
 
428
  }
429
  }
430
 
@@ -438,14 +461,20 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
438
 
439
  if ( $this->get_settings_handler()->is_product_sync_enabled() && ! $this->get_background_job_handler()->test_connection() ) {
440
 
441
- $this->get_admin_notice_handler()->add_admin_notice( sprintf(
442
- /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
443
- __( '%1$sWooCommerce Square:%2$s It looks like your site does not support background processing, which means large numbers of products may not sync successfully with Square. %3$sRead more here%4$s on how to resolve this.', 'woocommerce-square' ),
444
- '<strong>', '</strong>',
445
- '<a href="https://docs.woocommerce.com/document/woocommerce-square/#sync-issues" target="_blank">', '</a>'
446
- ), 'wc-square-background-processing', [
447
- 'notice_class' => 'notice-warning',
448
- ] );
 
 
 
 
 
 
449
  }
450
  }
451
 
@@ -459,7 +488,7 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
459
  return;
460
  }
461
 
462
- $refresh_token = '';
463
  $settings_handler = $this->get_settings_handler();
464
 
465
  if ( method_exists( $settings_handler, 'get_access_token' ) ) {
@@ -483,10 +512,10 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
483
  '</strong>'
484
  ),
485
  'wc-square-missing-refresh-token',
486
- [
487
  'dismissible' => false,
488
  'notice_class' => 'notice-error',
489
- ]
490
  );
491
  }
492
  }
@@ -508,12 +537,17 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
508
  // only show for products configured as taxable and sync with Square
509
  if ( $product instanceof \WC_Product && $product->is_taxable() && Product::is_synced_with_square( $product ) ) {
510
 
511
- $this->get_admin_notice_handler()->add_admin_notice( sprintf(
512
- __( '%1$sWooCommerce Square:%2$s Product prices are entered inclusive of tax, but Square does not support syncing tax-inclusive prices. Please make sure your Square tax rates match your WooCommerce tax rates.', 'woocommerce-square' ),
513
- '<strong>', '</strong>'
514
- ), 'wc-square-tax-inclusive', [
515
- 'notice_class' => 'notice-warning',
516
- ] );
 
 
 
 
 
517
  }
518
  }
519
  }
@@ -534,15 +568,21 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
534
 
535
  if ( $this->get_settings_handler()->get_location_id() === $location->getId() && get_woocommerce_currency() !== $location->getCurrency() ) {
536
 
537
- $this->get_admin_notice_handler()->add_admin_notice( sprintf(
538
- __( 'Heads up! Your store currency is %1$s but your configured Square business location currency is %2$s, so payments cannot be processed. Please %3$schoose a different business location%4$s or change your %5$sshop currency%6$s.', 'woocommerce-square' ),
539
- '<strong>' . esc_html( get_woocommerce_currency() ) . '</strong>',
540
- '<strong>' . esc_html( $location->getCurrency() ) . '</strong>',
541
- '<a href="' . esc_url( $this->get_settings_url() ) . '">', '</a>',
542
- '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings' ) ) . '">', '</a>'
543
- ), 'wc-square-currency-mismatch', [
544
- 'notice_class' => 'notice-error',
545
- ] );
 
 
 
 
 
 
546
  }
547
  }
548
  }
@@ -735,56 +775,77 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
735
  protected function get_deprecated_hooks() {
736
 
737
  // the following are filters, except when an action is explicitly mentioned
738
- $v2_0_0_removed_hooks = [
739
 
740
  // to filter the locale, the default WordPress filter should be used:
741
- 'woocommerce_square_plugin_locale' => [ 'replacement' => 'plugin_locale', 'map' => true ],
 
 
 
742
 
743
  // sync square product variation properties
744
- 'woocommerce_square_currency' => [], // used when passing a WooCommerce product price into a Square ItemVariation
745
- 'wc_square_sync_to_square_price' => [], // used when passing a WooCommerce product price into a Square ItemVariation
746
- 'woocommerce_square_format_price' => [], // formats the price coming from Square
747
- 'woocommerce_square_sync_from_square_description' => [], // flag whether to add a description to created item
748
 
749
  // we no longer filter inventory type in v2.0.0 and timeout is handled by background job differently
750
- 'woocommerce_square_inventory_type' => [],
751
- 'woocommerce_square_inventory_sync_timeout_limit' => [],
752
- 'woocommerce_square_inventory_poll_frequency' => [],
753
 
754
  // Square payment
755
- 'woocommerce_square_payment_form_trigger_element' => [],
756
- 'woocommerce_square_payment_order_note' => [ 'replacement' => 'wc_square_payment_order_note', 'map' => true ],
757
- 'woocommerce_square_description' => [], // front end payment fields description
 
 
 
758
 
759
  // most gateway properties and settings can be mapped to new filters:
760
- 'woocommerce_square_api_url' => [ 'replacement' => 'wc_square_api_url', 'map' => true ], // filter is reinstated with name change for consistency
761
- 'woocommerce_square_payment_gateway_is_available' => [ 'replacement' => 'wc_gateway_square_credit_card_is_available', 'map' => true ], // filter handled by SkyVerge Framework
762
- 'woocommerce_square_integration_settings_args' => [ 'replacement' => 'woocommerce_settings_api_form_fields_square', 'map' => true ], // filters gateway settings
763
- 'woocommerce_square_integration_custom_settings' => [ 'replacement' => 'woocommerce_settings_tabs_square', 'map' => true ], // settings action hook
 
 
 
 
 
 
 
 
 
 
 
 
764
 
765
  // API requests filters, these are handled differently and can't be mapped:
766
- 'woocommerce_square_request_args' => [],
767
- 'woocommerce_square_request_retries' => [],
768
 
769
  // transients are no longer used to handle these cache types:
770
- 'woocommerce_square_business_location_cache' => [],
771
- 'woocommerce_square_item_sku_cache' => [],
772
- 'woocommerce_square_inventory_cache' => [],
773
- 'woocommerce_square_sync_processing_ids_cache' => [],
774
- 'woocommerce_square_manual_sync_processing_cache' => [],
775
- 'woocommerce_square_syncing_square_ids_cache' => [],
776
- 'woocommerce_square_syncing_wc_product_ids_cache' => [],
777
 
778
  // when a bulk sync action is triggered (action hook):
779
- 'woocommerce_square_bulk_syncing_square_to_wc' => [],
780
 
781
  // get_posts args filter, the new implementation has different scope:
782
- 'woocommerce_square_get_all_product_ids_args' => [],
783
 
784
  // idempotency key
785
- 'woocommerce_square_idempotency_key' => [ 'replacement' => 'wc_square_idempotency_key', 'map' => true ],
 
 
 
786
 
787
- ];
788
 
789
  // add common array data for all removed hooks in version 2.0.0
790
  foreach ( array_keys( $v2_0_0_removed_hooks ) as $hook_name ) {
@@ -819,10 +880,10 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
819
  */
820
  public function get_settings_url( $gateway_id = null ) {
821
 
822
- $params = [
823
  'page' => 'wc-settings',
824
  'tab' => self::PLUGIN_ID,
825
- ];
826
 
827
  return add_query_arg( $params, admin_url( 'admin.php' ) );
828
  }
@@ -837,7 +898,7 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
837
  */
838
  public function get_sales_page_url() {
839
 
840
- return 'https://woocommerce.com/products/woocommerce-square/';
841
  }
842
 
843
 
@@ -854,6 +915,21 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
854
  }
855
 
856
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857
  /**
858
  * Gets the support URL.
859
  *
@@ -863,7 +939,7 @@ class Plugin extends Framework\SV_WC_Payment_Gateway_Plugin {
863
  */
864
  public function get_support_url() {
865
 
866
- return 'https://wordpress.org/support/plugin/woocommerce-square/'; // TODO: confirm this
867
  }
868
 
869
 
23
 
24
  namespace WooCommerce\Square;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Handlers\Background_Job;
42
 
43
 
44
  /** plugin version number */
45
+ const VERSION = '2.2.0';
46
 
47
  /** plugin ID */
48
  const PLUGIN_ID = 'square';
91
  parent::__construct(
92
  self::PLUGIN_ID,
93
  self::VERSION,
94
+ array(
95
+ 'text_domain' => 'woocommerce-square',
96
+ 'gateways' => array( self::GATEWAY_ID => Gateway::class ),
97
+ 'require_ssl' => true,
98
+ 'supports' => array(
99
  self::FEATURE_CAPTURE_CHARGE,
100
  self::FEATURE_CUSTOMER_ID,
101
  self::FEATURE_MY_PAYMENT_METHODS,
102
+ ),
103
+ 'dependencies' => array(
104
+ 'php_extensions' => array( 'curl', 'json', 'mbstring' ),
105
+ ),
106
+ )
107
  );
108
 
109
  $this->includes();
115
  */
116
  do_action( 'wc_square_loaded' );
117
 
118
+ add_action( 'woocommerce_register_taxonomy', array( $this, 'init_taxonomies' ) );
119
 
120
+ add_filter( 'woocommerce_locate_template', array( $this, 'locate_template' ), 20, 3 );
121
+ add_filter( 'woocommerce_locate_core_template', array( $this, 'locate_template' ), 20, 3 );
122
  }
123
 
124
 
158
  public function add_api_request_logging() {
159
 
160
  if ( ! has_action( 'wc_' . $this->get_id() . '_api_request_performed' ) ) {
161
+ add_action( 'wc_' . $this->get_id() . '_api_request_performed', array( $this, 'log_api_request' ), 10, 2 );
162
  }
163
  }
164
 
273
 
274
  $message = sprintf(
275
  __( 'Heads up! There may be a problem with your connection to Square. In order to continue accepting payments, please %1$sdisconnect and re-connect your site%2$s.', 'woocommerce-square' ),
276
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">',
277
+ '</a>'
278
  );
279
 
280
+ $this->get_admin_notice_handler()->add_admin_notice(
281
+ $message,
282
+ 'refresh-failed',
283
+ array(
284
+ 'dismissible' => false,
285
+ 'notice_class' => 'notice-warning',
286
+ )
287
+ );
288
  }
289
 
290
  if ( $this->get_settings_handler()->is_connected() ) {
303
  $instruction = sprintf(
304
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
305
  __( 'Visit the %1$splugin settings%2$s to set your business location.', 'woocommerce-square' ),
306
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">',
307
+ '</a>'
308
  );
309
  }
310
 
319
  $instruction = sprintf(
320
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - product count, %3$s - </strong> tag, %4$s - <a> tag, %5$s - </a> tag */
321
  __( '%1$s%2$d products%3$s are marked "sync with Square". %4$sStart a new sync now &raquo;%5$s', 'woocommerce-square' ),
322
+ '<strong>',
323
+ count( Product::get_products_synced_with_square() ),
324
+ '</strong>',
325
+ '<a href="' . esc_url( add_query_arg( 'section', 'update', $this->get_settings_url() ) ) . '">',
326
+ '</a>'
327
  );
328
 
329
  } else {
331
  $instruction = sprintf(
332
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
333
  __( '%1$sNo products%2$s are marked "sync with Square". %3$sUpdate your products to sync data &raquo;%4$s', 'woocommerce-square' ),
334
+ '<strong>',
335
+ '</strong>',
336
+ '<a href="' . esc_url( admin_url( 'edit.php?post_type=product' ) ) . '">',
337
+ '</a>'
338
  );
339
  }
340
 
346
 
347
  $message = sprintf(
348
  __( 'Heads up! Square is configured to sync product inventory, but WooCommerce stock management is disabled. Please %1$senable stock management%2$s to ensure product inventory counts are kept in sync.', 'woocommerce-square' ),
349
+ '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=products&section=inventory' ) ) . '">',
350
+ '</a>'
351
  );
352
 
353
+ $this->get_admin_notice_handler()->add_admin_notice(
354
+ $message,
355
+ 'enable-wc-sync',
356
+ array(
357
+ 'notice_class' => 'notice-warning',
358
+ )
359
+ );
360
  }
 
361
  } else {
362
 
363
  if ( $this->is_plugin_settings() ) {
369
  $instruction = sprintf(
370
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
371
  __( 'To get started, %1$sconnect with Square &raquo;%2$s', 'woocommerce-square' ),
372
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">',
373
+ '</a>'
374
  );
375
  }
376
 
403
  esc_html__( '%1$s has been updated to version %2$s. In order to continue syncing product inventory, please make sure to disconnect and reconnect with Square from the %3$splugin settings%4$s and re-sync your products. Read more in the %5$supdated documentation%6$s.', 'woocommerce-square' ),
404
  '<strong>' . esc_html( $this->get_plugin_name() ) . '</strong>',
405
  $this->get_version(),
406
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">',
407
+ '</a>',
408
+ '<a href="' . esc_url( $this->get_documentation_url() ) . '">',
409
+ '</a>'
410
  ),
411
  'updated-to-v2',
412
+ array( 'notice_class' => 'notice-warning' )
413
  );
414
  }
415
  }
422
  */
423
  protected function add_base_location_admin_notice() {
424
 
425
+ $accepted_countries = array(
426
  'US',
427
  'CA',
428
  'GB',
429
  'AU',
430
  'JP',
431
+ );
432
 
433
  $base_location = wc_get_base_location();
434
 
435
  if ( isset( $base_location['country'] ) && ! in_array( $base_location['country'], $accepted_countries, true ) ) {
436
 
437
+ $this->get_admin_notice_handler()->add_admin_notice(
438
+ sprintf(
439
+ /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - 2-character country code, %4$s - comma separated list of 2-character country codes */
440
+ __( '%1$sWooCommerce Square:%2$s Your base country is %3$s, but Square can’t accept transactions from merchants outside of %4$s.', 'woocommerce-square' ),
441
+ '<strong>',
442
+ '</strong>',
443
+ esc_html( $base_location['country'] ),
444
+ esc_html( Framework\SV_WC_Helper::list_array_items( $accepted_countries ) )
445
+ ),
446
+ 'wc-square-base-location',
447
+ array(
448
+ 'notice_class' => 'notice-error',
449
+ )
450
+ );
451
  }
452
  }
453
 
461
 
462
  if ( $this->get_settings_handler()->is_product_sync_enabled() && ! $this->get_background_job_handler()->test_connection() ) {
463
 
464
+ $this->get_admin_notice_handler()->add_admin_notice(
465
+ sprintf(
466
+ /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
467
+ __( '%1$sWooCommerce Square:%2$s It looks like your site does not support background processing, which means large numbers of products may not sync successfully with Square. %3$sRead more here%4$s on how to resolve this.', 'woocommerce-square' ),
468
+ '<strong>',
469
+ '</strong>',
470
+ '<a href="https://docs.woocommerce.com/document/woocommerce-square/#sync-issues" target="_blank">',
471
+ '</a>'
472
+ ),
473
+ 'wc-square-background-processing',
474
+ array(
475
+ 'notice_class' => 'notice-warning',
476
+ )
477
+ );
478
  }
479
  }
480
 
488
  return;
489
  }
490
 
491
+ $refresh_token = '';
492
  $settings_handler = $this->get_settings_handler();
493
 
494
  if ( method_exists( $settings_handler, 'get_access_token' ) ) {
512
  '</strong>'
513
  ),
514
  'wc-square-missing-refresh-token',
515
+ array(
516
  'dismissible' => false,
517
  'notice_class' => 'notice-error',
518
+ )
519
  );
520
  }
521
  }
537
  // only show for products configured as taxable and sync with Square
538
  if ( $product instanceof \WC_Product && $product->is_taxable() && Product::is_synced_with_square( $product ) ) {
539
 
540
+ $this->get_admin_notice_handler()->add_admin_notice(
541
+ sprintf(
542
+ __( '%1$sWooCommerce Square:%2$s Product prices are entered inclusive of tax, but Square does not support syncing tax-inclusive prices. Please make sure your Square tax rates match your WooCommerce tax rates.', 'woocommerce-square' ),
543
+ '<strong>',
544
+ '</strong>'
545
+ ),
546
+ 'wc-square-tax-inclusive',
547
+ array(
548
+ 'notice_class' => 'notice-warning',
549
+ )
550
+ );
551
  }
552
  }
553
  }
568
 
569
  if ( $this->get_settings_handler()->get_location_id() === $location->getId() && get_woocommerce_currency() !== $location->getCurrency() ) {
570
 
571
+ $this->get_admin_notice_handler()->add_admin_notice(
572
+ sprintf(
573
+ __( 'Heads up! Your store currency is %1$s but your configured Square business location currency is %2$s, so payments cannot be processed. Please %3$schoose a different business location%4$s or change your %5$sshop currency%6$s.', 'woocommerce-square' ),
574
+ '<strong>' . esc_html( get_woocommerce_currency() ) . '</strong>',
575
+ '<strong>' . esc_html( $location->getCurrency() ) . '</strong>',
576
+ '<a href="' . esc_url( $this->get_settings_url() ) . '">',
577
+ '</a>',
578
+ '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings' ) ) . '">',
579
+ '</a>'
580
+ ),
581
+ 'wc-square-currency-mismatch',
582
+ array(
583
+ 'notice_class' => 'notice-error',
584
+ )
585
+ );
586
  }
587
  }
588
  }
775
  protected function get_deprecated_hooks() {
776
 
777
  // the following are filters, except when an action is explicitly mentioned
778
+ $v2_0_0_removed_hooks = array(
779
 
780
  // to filter the locale, the default WordPress filter should be used:
781
+ 'woocommerce_square_plugin_locale' => array(
782
+ 'replacement' => 'plugin_locale',
783
+ 'map' => true,
784
+ ),
785
 
786
  // sync square product variation properties
787
+ 'woocommerce_square_currency' => array(), // used when passing a WooCommerce product price into a Square ItemVariation
788
+ 'wc_square_sync_to_square_price' => array(), // used when passing a WooCommerce product price into a Square ItemVariation
789
+ 'woocommerce_square_format_price' => array(), // formats the price coming from Square
790
+ 'woocommerce_square_sync_from_square_description' => array(), // flag whether to add a description to created item
791
 
792
  // we no longer filter inventory type in v2.0.0 and timeout is handled by background job differently
793
+ 'woocommerce_square_inventory_type' => array(),
794
+ 'woocommerce_square_inventory_sync_timeout_limit' => array(),
795
+ 'woocommerce_square_inventory_poll_frequency' => array(),
796
 
797
  // Square payment
798
+ 'woocommerce_square_payment_form_trigger_element' => array(),
799
+ 'woocommerce_square_payment_order_note' => array(
800
+ 'replacement' => 'wc_square_payment_order_note',
801
+ 'map' => true,
802
+ ),
803
+ 'woocommerce_square_description' => array(), // front end payment fields description
804
 
805
  // most gateway properties and settings can be mapped to new filters:
806
+ 'woocommerce_square_api_url' => array(
807
+ 'replacement' => 'wc_square_api_url',
808
+ 'map' => true,
809
+ ), // filter is reinstated with name change for consistency
810
+ 'woocommerce_square_payment_gateway_is_available' => array(
811
+ 'replacement' => 'wc_gateway_square_credit_card_is_available',
812
+ 'map' => true,
813
+ ), // filter handled by SkyVerge Framework
814
+ 'woocommerce_square_integration_settings_args' => array(
815
+ 'replacement' => 'woocommerce_settings_api_form_fields_square',
816
+ 'map' => true,
817
+ ), // filters gateway settings
818
+ 'woocommerce_square_integration_custom_settings' => array(
819
+ 'replacement' => 'woocommerce_settings_tabs_square',
820
+ 'map' => true,
821
+ ), // settings action hook
822
 
823
  // API requests filters, these are handled differently and can't be mapped:
824
+ 'woocommerce_square_request_args' => array(),
825
+ 'woocommerce_square_request_retries' => array(),
826
 
827
  // transients are no longer used to handle these cache types:
828
+ 'woocommerce_square_business_location_cache' => array(),
829
+ 'woocommerce_square_item_sku_cache' => array(),
830
+ 'woocommerce_square_inventory_cache' => array(),
831
+ 'woocommerce_square_sync_processing_ids_cache' => array(),
832
+ 'woocommerce_square_manual_sync_processing_cache' => array(),
833
+ 'woocommerce_square_syncing_square_ids_cache' => array(),
834
+ 'woocommerce_square_syncing_wc_product_ids_cache' => array(),
835
 
836
  // when a bulk sync action is triggered (action hook):
837
+ 'woocommerce_square_bulk_syncing_square_to_wc' => array(),
838
 
839
  // get_posts args filter, the new implementation has different scope:
840
+ 'woocommerce_square_get_all_product_ids_args' => array(),
841
 
842
  // idempotency key
843
+ 'woocommerce_square_idempotency_key' => array(
844
+ 'replacement' => 'wc_square_idempotency_key',
845
+ 'map' => true,
846
+ ),
847
 
848
+ );
849
 
850
  // add common array data for all removed hooks in version 2.0.0
851
  foreach ( array_keys( $v2_0_0_removed_hooks ) as $hook_name ) {
880
  */
881
  public function get_settings_url( $gateway_id = null ) {
882
 
883
+ $params = array(
884
  'page' => 'wc-settings',
885
  'tab' => self::PLUGIN_ID,
886
+ );
887
 
888
  return add_query_arg( $params, admin_url( 'admin.php' ) );
889
  }
898
  */
899
  public function get_sales_page_url() {
900
 
901
+ return 'https://woocommerce.com/products/square/';
902
  }
903
 
904
 
915
  }
916
 
917
 
918
+ /**
919
+ * Gets the plugin reviews page URL.
920
+ *
921
+ * Used for the 'Reviews' plugin action and review prompts.
922
+ *
923
+ * @since 2.1.7
924
+ *
925
+ * @return string
926
+ */
927
+ public function get_reviews_url() {
928
+
929
+ return $this->get_sales_page_url() ? $this->get_sales_page_url() . '#comments' : '';
930
+ }
931
+
932
+
933
  /**
934
  * Gets the support URL.
935
  *
939
  */
940
  public function get_support_url() {
941
 
942
+ return 'https://woocommerce.com/my-account/create-a-ticket/?select=1770503';
943
  }
944
 
945
 
includes/Settings.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -39,26 +39,54 @@ use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
39
  class Settings extends \WC_Settings_API {
40
 
41
 
42
- /** @var string square system of record indicator */
 
 
 
 
43
  const SYSTEM_OF_RECORD_SQUARE = 'square';
44
 
45
- /** @var string square system of record indicator */
 
 
 
 
46
  const SYSTEM_OF_RECORD_WOOCOMMERCE = 'woocommerce';
47
 
48
- /** @var string system of record indicator for disabled sync */
 
 
 
 
49
  const SYSTEM_OF_RECORD_DISABLED = 'disabled';
50
 
51
 
52
- /** @var string un-encrypted refresh token */
 
 
 
 
53
  protected $refresh_token;
54
 
55
- /** @var string un-encrypted access token */
 
 
 
 
56
  protected $access_token;
57
 
58
- /** @var array business locations returned by the API */
 
 
 
 
59
  protected $locations;
60
 
61
- /** @var Plugin plugin instance */
 
 
 
 
62
  protected $plugin;
63
 
64
 
@@ -67,7 +95,7 @@ class Settings extends \WC_Settings_API {
67
  *
68
  * @since 2.0.0
69
  *
70
- * @param Plugin $plugin plugin instance
71
  */
72
  public function __construct( Plugin $plugin ) {
73
 
@@ -79,27 +107,24 @@ class Settings extends \WC_Settings_API {
79
 
80
  $this->init_settings();
81
 
82
- // remove some of our custom fields that shouldn't be saved
83
- add_action( 'woocommerce_settings_api_sanitized_fields_' . $this->id, function( $fields ) {
 
 
84
 
85
- unset( $fields['general'], $fields['connect'], $fields['import_products'] );
86
 
87
- return $fields;
88
- } );
89
-
90
- // Save sandbox token.
91
- if ( $this->is_sandbox() ) {
92
- add_action(
93
- 'woocommerce_settings_api_sanitized_fields_' . $this->id,
94
- function( $fields ) {
95
  $this->update_access_token( $fields['sandbox_token'] );
96
- $this->access_token = false; // Remove encrypted token.
97
  $this->refresh_token = false; // Remove encrypted token.
98
- $this->init_form_fields(); // Reload form fields after saving token.
99
- return $fields;
100
  }
101
- );
102
- }
 
 
 
 
103
  }
104
 
105
 
@@ -115,7 +140,8 @@ class Settings extends \WC_Settings_API {
115
  $general_description = sprintf(
116
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
117
  __( 'Sync your products and inventory and also accept credit and debit card payments at checkout. %1$sClick here%2$s to configure payments.', 'woocommerce-square' ),
118
- '<a href="' . esc_url( $this->get_plugin()->get_payment_gateway_configuration_url( $this->get_plugin()->get_gateway()->get_id() ) ) . '">', '</a>'
 
119
  );
120
 
121
  } else {
@@ -123,125 +149,133 @@ class Settings extends \WC_Settings_API {
123
  $general_description = __( 'Connect with Square to start syncing your products and inventory and also accept credit and debit card payments at checkout.', 'woocommerce-square' );
124
  }
125
 
126
- $fields = [
127
- 'general' => [
128
  'type' => 'title',
129
  'description' => $general_description,
130
- ],
131
- ];
132
 
133
- $fields['enable_sandbox'] = [
134
  'title' => __( 'Enable Sandbox Mode', 'woocommerce-square' ),
135
  'label' => '<span>' . __( 'Enable to set the plugin in sandbox mode.', 'woocommerce-square' ) . '</span>',
136
  'type' => 'checkbox',
137
  'description' => __( 'After enabling you’ll see a new Sandbox settings section with two fields; Sandbox Application ID & Sandbox Access Token.', 'woocommerce-square' ),
138
- ];
139
 
140
- if ( $this->is_sandbox() ) {
141
- $fields['sandbox_settings'] = [
142
- 'type' => 'title',
143
- 'title' => __( 'Sandbox settings', 'woocommerce-square' ),
144
- 'description' => sprintf(
145
- // translators: Placeholders: %1$s - URL
146
- __( 'Sandbox details can be created at: %s', 'woocommerce-square' ),
147
- sprintf( '<a href="%1$s">%1$s</a>', 'https://developer.squareup.com/apps' )
148
- ),
149
- ];
150
- $fields['sandbox_application_id'] = [
151
- 'type' => 'input',
152
- 'title' => __( 'Sandbox Application ID', 'woocommerce-square' ),
153
- 'description' => __( 'Application ID for the Sandbox Application, see the details in the My Applications section.', 'woocommerce-square' ),
154
- ];
155
- $fields['sandbox_token'] = [
156
- 'type' => 'input',
157
- 'title' => __( 'Sandbox Access Token', 'woocommerce-square' ),
158
- 'description' => __( 'Access Token for the Sandbox Test Account, see the details in the Sandbox Test Account section. Make sure you use the correct Sandbox Access Token for your application. For a given Sandbox Test Account, each Authorized Application is assigned a different Access Token.', 'woocommerce-square' ),
159
- ];
160
- }
161
 
162
- // display these fields only if connected
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  if ( $this->is_connected() ) {
164
 
165
- $fields['location_id'] = [
166
  'title' => __( 'Business location', 'woocommerce-square' ),
167
  'type' => 'select',
168
  'class' => 'wc-enhanced-select',
169
  'description' => sprintf(
170
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
171
  __( 'Select a location to link to this site. Only %1$sactive%2$s %3$slocations%4$s that support credit card processing in Square can be linked.', 'woocommerce-square' ),
172
- '<strong>', '</strong>',
173
- '<a href="https://docs.woocommerce.com/document/woocommerce-square/#section-4" target="_blank">', '</a>'
 
 
174
  ),
175
- 'options' => [], // this is populated on display
176
- ];
177
 
178
- $fields['system_of_record'] = [
179
  'title' => __( 'Product system of record', 'woocommerce-square' ),
180
  'type' => 'select',
181
  'class' => 'wc-enhanced-select',
182
  'description' => sprintf(
183
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
184
  __( 'Choose where you will update data for synced products. Inventory in Square is %1$salways%2$s checked for adjustments when sync is enabled. %3$sClick here%4$s to read more about choosing a system of record.', 'woocommerce-square' ),
185
- '<strong>', '</strong>',
186
- '<a href="' . esc_url( wc_square()->get_documentation_url() ) . '#sync">', '</a>'
 
 
187
  ),
188
- 'options' => [
189
  self::SYSTEM_OF_RECORD_DISABLED => __( 'Do not sync product data', 'woocommerce-square' ),
190
  self::SYSTEM_OF_RECORD_SQUARE => __( 'Square', 'woocommerce-square' ),
191
  self::SYSTEM_OF_RECORD_WOOCOMMERCE => __( 'WooCommerce', 'woocommerce-square' ),
192
- ],
193
- 'default' => 'disabled',
194
- ];
195
 
196
- $fields['enable_inventory_sync'] = [
197
  'title' => __( 'Sync inventory', 'woocommerce-square' ),
198
  'label' => '<span>' . __( 'Enable to sync product inventory with Square', 'woocommerce-square' ) . '</span>',
199
  'type' => 'checkbox',
200
  'description' => __( 'Inventory is fetched from Square periodically and updated in WooCommerce', 'woocommerce-square' ),
201
- ];
202
 
203
- $fields['hide_missing_products'] = [
204
  'title' => __( 'Handle missing products', 'woocommerce-square' ),
205
  'label' => __( 'Hide synced products when not found in Square', 'woocommerce-square' ),
206
  'type' => 'checkbox',
207
  'description' => __( 'Products not found in Square will be hidden in the WooCommerce product catalog.', 'woocommerce-square' ),
208
- ];
209
 
210
- $fields['import_products'] = [
211
  'title' => __( 'Import Products', 'woocommerce-square' ),
212
  'type' => 'import_products',
213
  'desc_tip' => __( 'Run an import to create new products in this WooCommerce store for each new product created in Square that has a unique SKU not existing in here. Needs to be run each time new items are created in Square.', 'woocommerce-square' ),
214
- ];
215
  }
216
 
217
  // In sandbox mode we don't want to intially display the connect button, only disconnect.
218
  if ( ! ( $this->is_sandbox() && ! $this->is_connected() ) ) {
219
  $fields = array_merge(
220
  $fields,
221
- [
222
- 'connect' => [
223
  'title' => __( 'Connection', 'woocommerce-square' ),
224
  'type' => 'connect',
225
  'desc_tip' => '',
226
- ],
227
- ]
228
  );
229
  }
230
 
231
  // Always display these fields.
232
  $fields = array_merge(
233
  $fields,
234
- [
235
- 'debug_logging_enabled' => [
236
  'title' => __( 'Enable Logging', 'woocommerce-square' ),
237
  'type' => 'checkbox',
238
  'label' => sprintf(
239
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
240
  __( 'Log debug messages to the %1$sWooCommerce status log%2$s', 'woocommerce-square' ),
241
- '<a href="' . esc_url( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) . '">', '</a>'
 
242
  ),
243
- ],
244
- ]
245
  );
246
 
247
  $this->form_fields = $fields;
@@ -261,36 +295,60 @@ class Settings extends \WC_Settings_API {
261
 
262
  $fields = parent::get_form_fields();
263
 
264
- if ( ! empty( $fields['location_id'] ) && did_action( 'wc_square_initialized' ) && $this->is_admin_settings_screen() ) {
265
- $locations = [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  '' => __( 'Please choose a location', 'woocommerce-square' ),
267
- ];
268
 
269
  if ( ! empty( $this->get_locations() ) ) {
270
-
271
  foreach ( $this->get_locations() as $location ) {
272
-
273
  if ( 'ACTIVE' === $location->getStatus() && in_array( 'CREDIT_CARD_PROCESSING', (array) $location->getCapabilities(), true ) ) {
274
  $locations[ $location->getId() ] = $location->getName();
275
  }
276
  }
277
  }
278
 
279
- $fields['location_id']['options'] = $locations;
280
-
281
  }
282
 
283
  return $fields;
284
  }
285
 
286
 
 
 
 
 
 
 
287
  public function generate_import_products_html( $id, $field ) {
288
 
289
  ob_start();
290
  ?>
291
  <tr valign="top">
292
  <th scope="row" class="titledesc">
293
- <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); ?></label>
294
  </th>
295
  <td class="forminp">
296
  <a href='#' class='button js-import-square-products'>
@@ -309,8 +367,8 @@ class Settings extends \WC_Settings_API {
309
  *
310
  * @since 2.0.0
311
  *
312
- * @param string $id field ID
313
- * @param array $field field data
314
  * @return string
315
  */
316
  public function generate_connect_html( $id, $field ) {
@@ -319,14 +377,16 @@ class Settings extends \WC_Settings_API {
319
  ?>
320
  <tr valign="top">
321
  <th scope="row" class="titledesc">
322
- <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); ?></label>
323
  </th>
324
  <td class="forminp">
325
- <?php if ( $this->get_access_token() ) {
326
- echo $this->get_plugin()->get_connection_handler()->get_disconnect_button_html();
 
327
  } else {
328
- echo $this->get_plugin()->get_connection_handler()->get_connect_button_html( $this->is_sandbox() );
329
- } ?>
 
330
  </td>
331
  </tr>
332
  <?php
@@ -340,12 +400,12 @@ class Settings extends \WC_Settings_API {
340
  *
341
  * @since 2.0.0
342
  *
343
- * @param string $token refresh token
344
  */
345
  public function update_refresh_token( $token ) {
346
 
347
  $refresh_tokens = $this->get_refresh_tokens();
348
- $environment = $this->get_environment();
349
 
350
  if ( ! empty( $token ) ) {
351
 
@@ -361,7 +421,7 @@ class Settings extends \WC_Settings_API {
361
 
362
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
363
 
364
- // log the event, but don't halt the process
365
  $this->get_plugin()->log( 'Could not encrypt refresh token. ' . $exception->getMessage() );
366
  }
367
  }
@@ -378,7 +438,7 @@ class Settings extends \WC_Settings_API {
378
  *
379
  * @since 2.0.0
380
  *
381
- * @param string $token access token
382
  */
383
  public function update_access_token( $token ) {
384
 
@@ -399,12 +459,15 @@ class Settings extends \WC_Settings_API {
399
 
400
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
401
 
402
- // log the event, but don't halt the process
403
  $this->get_plugin()->log( 'Could not encrypt access token. ' . $exception->getMessage() );
404
  }
405
  }
406
 
407
  $access_tokens[ $environment ] = $token;
 
 
 
408
  }
409
 
410
  update_option( 'wc_square_access_tokens', $access_tokens );
@@ -441,9 +504,9 @@ class Settings extends \WC_Settings_API {
441
  */
442
  public function clear_location_id() {
443
 
444
- $settings = get_option( $this->get_option_key(), [] );
445
 
446
- $settings['location_id'] = '';
447
 
448
  update_option( $this->get_option_key(), $settings );
449
  }
@@ -595,8 +658,18 @@ class Settings extends \WC_Settings_API {
595
  * @return string
596
  */
597
  public function get_location_id() {
 
 
 
 
598
 
599
- return $this->get_option( 'location_id' );
 
 
 
 
 
 
600
  }
601
 
602
 
@@ -609,13 +682,12 @@ class Settings extends \WC_Settings_API {
609
  */
610
  public function get_locations() {
611
 
612
- // if locations have already been fetched, no need to fetch again
613
  if ( is_array( $this->locations ) ) {
614
 
615
  return $this->locations;
616
  }
617
 
618
- // don't always need to refetch when not on Settings screen
619
  if ( ! $this->is_admin_settings_screen() ) {
620
 
621
  $this->locations = get_transient( 'wc_square_locations' );
@@ -623,15 +695,15 @@ class Settings extends \WC_Settings_API {
623
 
624
  if ( ! is_array( $this->locations ) && did_action( 'wc_square_initialized' ) ) {
625
 
626
- $this->locations = [];
627
 
628
  try {
629
 
630
- // cache the locations returned so they can be used elsewhere
631
  $this->locations = $this->get_plugin()->get_api( $this->get_access_token(), $this->is_sandbox() )->get_locations();
632
  set_transient( 'wc_square_locations', $this->locations, HOUR_IN_SECONDS );
633
 
634
- // check the returned IDs against what's currently configured
635
  $stored_location_id = $this->get_location_id();
636
  $found = ! $stored_location_id;
637
 
@@ -643,11 +715,10 @@ class Settings extends \WC_Settings_API {
643
  }
644
  }
645
 
646
- // if the currently set location ID is not present in the connected account's available locations, clear it locally
647
  if ( ! $found ) {
648
  $this->clear_location_id();
649
  }
650
-
651
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
652
 
653
  $this->get_plugin()->log( 'Could not retrieve business locations.' );
@@ -682,15 +753,15 @@ class Settings extends \WC_Settings_API {
682
 
683
  switch ( $this->get_system_of_record() ) {
684
 
685
- case 'square' :
686
  $sor = __( 'Square', 'woocommerce-square' );
687
- break;
688
- case 'woocommerce' :
689
  $sor = __( 'WooCommerce', 'woocommerce-square' );
690
- break;
691
- default :
692
  $sor = '';
693
- break;
694
  }
695
 
696
  return $sor;
@@ -724,7 +795,7 @@ class Settings extends \WC_Settings_API {
724
 
725
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
726
 
727
- // log the event, but don't halt the process
728
  $this->get_plugin()->log( 'Could not decrypt refresh token. ' . $exception->getMessage() );
729
  }
730
  }
@@ -751,7 +822,7 @@ class Settings extends \WC_Settings_API {
751
  */
752
  public function get_access_token() {
753
 
754
- if ( empty( $this->access_token ) ) {
755
 
756
  $tokens = $this->get_access_tokens();
757
  $token = null;
@@ -770,7 +841,7 @@ class Settings extends \WC_Settings_API {
770
 
771
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
772
 
773
- // log the event, but don't halt the process
774
  $this->get_plugin()->log( 'Could not decrypt access token. ' . $exception->getMessage() );
775
  }
776
  }
@@ -799,7 +870,7 @@ class Settings extends \WC_Settings_API {
799
  * @return array
800
  */
801
  public function get_access_tokens() {
802
- return (array) get_option( 'wc_square_access_tokens', [] );
803
  }
804
 
805
 
@@ -813,7 +884,7 @@ class Settings extends \WC_Settings_API {
813
  * @return array
814
  */
815
  public function get_refresh_tokens() {
816
- return (array) get_option( 'wc_square_refresh_tokens', [] );
817
  }
818
 
819
  /**
@@ -835,8 +906,7 @@ class Settings extends \WC_Settings_API {
835
  * @return boolean
836
  */
837
  public function is_sandbox_setting_enabled() {
838
- // we also need to check the checkbox as it is not store on the first load after submission.
839
- return 'yes' == $this->get_enable_sandbox() || isset( $_POST['wc_square_enable_sandbox'] );
840
  }
841
 
842
 
@@ -872,6 +942,6 @@ class Settings extends \WC_Settings_API {
872
  * @return bool True if the current request is for the Square admin settings, otherwise false.
873
  */
874
  public function is_admin_settings_screen() {
875
- return isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && Plugin::PLUGIN_ID === $_GET['tab'];
876
  }
877
  }
23
 
24
  namespace WooCommerce\Square;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
39
  class Settings extends \WC_Settings_API {
40
 
41
 
42
+ /**
43
+ * Square system of record.
44
+ *
45
+ * @var string square system of record indicator
46
+ */
47
  const SYSTEM_OF_RECORD_SQUARE = 'square';
48
 
49
+ /**
50
+ * Woocommerce system of record.
51
+ *
52
+ * @var string square system of record indicator
53
+ */
54
  const SYSTEM_OF_RECORD_WOOCOMMERCE = 'woocommerce';
55
 
56
+ /**
57
+ * Disabled system of record.
58
+ *
59
+ * @var string system of record indicator for disabled sync
60
+ */
61
  const SYSTEM_OF_RECORD_DISABLED = 'disabled';
62
 
63
 
64
+ /**
65
+ * Refresh token
66
+ *
67
+ * @var string un-encrypted refresh token
68
+ */
69
  protected $refresh_token;
70
 
71
+ /**
72
+ * Access token
73
+ *
74
+ * @var string un-encrypted access token
75
+ */
76
  protected $access_token;
77
 
78
+ /**
79
+ * Square business locations
80
+ *
81
+ * @var array business locations returned by the API
82
+ */
83
  protected $locations;
84
 
85
+ /**
86
+ * Square plugin instance
87
+ *
88
+ * @var Plugin plugin instance
89
+ */
90
  protected $plugin;
91
 
92
 
95
  *
96
  * @since 2.0.0
97
  *
98
+ * @param Plugin $plugin plugin instance.
99
  */
100
  public function __construct( Plugin $plugin ) {
101
 
107
 
108
  $this->init_settings();
109
 
110
+ // remove some of our custom fields that shouldn't be saved.
111
+ add_action(
112
+ 'woocommerce_settings_api_sanitized_fields_' . $this->id,
113
+ function( $fields ) {
114
 
115
+ unset( $fields['general'], $fields['connect'], $fields['import_products'] );
116
 
117
+ if ( $this->is_sandbox() ) {
 
 
 
 
 
 
 
118
  $this->update_access_token( $fields['sandbox_token'] );
119
+ $this->access_token = false; // Remove encrypted token.
120
  $this->refresh_token = false; // Remove encrypted token.
 
 
121
  }
122
+
123
+ $this->init_form_fields(); // Reload form fields after saving token.
124
+
125
+ return $fields;
126
+ }
127
+ );
128
  }
129
 
130
 
140
  $general_description = sprintf(
141
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
142
  __( 'Sync your products and inventory and also accept credit and debit card payments at checkout. %1$sClick here%2$s to configure payments.', 'woocommerce-square' ),
143
+ '<a href="' . esc_url( $this->get_plugin()->get_payment_gateway_configuration_url( $this->get_plugin()->get_gateway()->get_id() ) ) . '">',
144
+ '</a>'
145
  );
146
 
147
  } else {
149
  $general_description = __( 'Connect with Square to start syncing your products and inventory and also accept credit and debit card payments at checkout.', 'woocommerce-square' );
150
  }
151
 
152
+ $fields = array(
153
+ 'general' => array(
154
  'type' => 'title',
155
  'description' => $general_description,
156
+ ),
157
+ );
158
 
159
+ $fields['enable_sandbox'] = array(
160
  'title' => __( 'Enable Sandbox Mode', 'woocommerce-square' ),
161
  'label' => '<span>' . __( 'Enable to set the plugin in sandbox mode.', 'woocommerce-square' ) . '</span>',
162
  'type' => 'checkbox',
163
  'description' => __( 'After enabling you’ll see a new Sandbox settings section with two fields; Sandbox Application ID & Sandbox Access Token.', 'woocommerce-square' ),
164
+ );
165
 
166
+ $fields['sandbox_settings'] = array(
167
+ 'type' => 'title',
168
+ 'title' => __( 'Sandbox settings', 'woocommerce-square' ),
169
+ 'id' => 'wc_square_sandbox_settings',
170
+ 'description' => sprintf(
171
+ // translators: Placeholders: %1$s - URL.
172
+ __( 'Sandbox details can be created at: %s', 'woocommerce-square' ),
173
+ sprintf( '<a href="%1$s">%1$s</a>', 'https://developer.squareup.com/apps' )
174
+ ),
175
+ );
 
 
 
 
 
 
 
 
 
 
 
176
 
177
+ $fields['sandbox_application_id'] = array(
178
+ 'type' => 'input',
179
+ 'title' => __( 'Sandbox Application ID', 'woocommerce-square' ),
180
+ 'class' => 'wc_square_sandbox_settings',
181
+ 'description' => __( 'Application ID for the Sandbox Application, see the details in the My Applications section.', 'woocommerce-square' ),
182
+ );
183
+
184
+ $fields['sandbox_token'] = array(
185
+ 'type' => 'input',
186
+ 'title' => __( 'Sandbox Access Token', 'woocommerce-square' ),
187
+ 'class' => 'wc_square_sandbox_settings',
188
+ 'description' => __( 'Access Token for the Sandbox Test Account, see the details in the Sandbox Test Account section. Make sure you use the correct Sandbox Access Token for your application. For a given Sandbox Test Account, each Authorized Application is assigned a different Access Token.', 'woocommerce-square' ),
189
+ );
190
+
191
+ // display these fields only if connected.
192
  if ( $this->is_connected() ) {
193
 
194
+ $fields[ $this->get_environment() . '_location_id' ] = array(
195
  'title' => __( 'Business location', 'woocommerce-square' ),
196
  'type' => 'select',
197
  'class' => 'wc-enhanced-select',
198
  'description' => sprintf(
199
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
200
  __( 'Select a location to link to this site. Only %1$sactive%2$s %3$slocations%4$s that support credit card processing in Square can be linked.', 'woocommerce-square' ),
201
+ '<strong>',
202
+ '</strong>',
203
+ '<a href="https://docs.woocommerce.com/document/woocommerce-square/#section-4" target="_blank">',
204
+ '</a>'
205
  ),
206
+ 'options' => array(), // this is populated on display.
207
+ );
208
 
209
+ $fields['system_of_record'] = array(
210
  'title' => __( 'Product system of record', 'woocommerce-square' ),
211
  'type' => 'select',
212
  'class' => 'wc-enhanced-select',
213
  'description' => sprintf(
214
  /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
215
  __( 'Choose where you will update data for synced products. Inventory in Square is %1$salways%2$s checked for adjustments when sync is enabled. %3$sClick here%4$s to read more about choosing a system of record.', 'woocommerce-square' ),
216
+ '<strong>',
217
+ '</strong>',
218
+ '<a href="' . esc_url( wc_square()->get_documentation_url() ) . '#section-7">',
219
+ '</a>'
220
  ),
221
+ 'options' => array(
222
  self::SYSTEM_OF_RECORD_DISABLED => __( 'Do not sync product data', 'woocommerce-square' ),
223
  self::SYSTEM_OF_RECORD_SQUARE => __( 'Square', 'woocommerce-square' ),
224
  self::SYSTEM_OF_RECORD_WOOCOMMERCE => __( 'WooCommerce', 'woocommerce-square' ),
225
+ ),
226
+ 'default' => 'disabled',
227
+ );
228
 
229
+ $fields['enable_inventory_sync'] = array(
230
  'title' => __( 'Sync inventory', 'woocommerce-square' ),
231
  'label' => '<span>' . __( 'Enable to sync product inventory with Square', 'woocommerce-square' ) . '</span>',
232
  'type' => 'checkbox',
233
  'description' => __( 'Inventory is fetched from Square periodically and updated in WooCommerce', 'woocommerce-square' ),
234
+ );
235
 
236
+ $fields['hide_missing_products'] = array(
237
  'title' => __( 'Handle missing products', 'woocommerce-square' ),
238
  'label' => __( 'Hide synced products when not found in Square', 'woocommerce-square' ),
239
  'type' => 'checkbox',
240
  'description' => __( 'Products not found in Square will be hidden in the WooCommerce product catalog.', 'woocommerce-square' ),
241
+ );
242
 
243
+ $fields['import_products'] = array(
244
  'title' => __( 'Import Products', 'woocommerce-square' ),
245
  'type' => 'import_products',
246
  'desc_tip' => __( 'Run an import to create new products in this WooCommerce store for each new product created in Square that has a unique SKU not existing in here. Needs to be run each time new items are created in Square.', 'woocommerce-square' ),
247
+ );
248
  }
249
 
250
  // In sandbox mode we don't want to intially display the connect button, only disconnect.
251
  if ( ! ( $this->is_sandbox() && ! $this->is_connected() ) ) {
252
  $fields = array_merge(
253
  $fields,
254
+ array(
255
+ 'connect' => array(
256
  'title' => __( 'Connection', 'woocommerce-square' ),
257
  'type' => 'connect',
258
  'desc_tip' => '',
259
+ ),
260
+ )
261
  );
262
  }
263
 
264
  // Always display these fields.
265
  $fields = array_merge(
266
  $fields,
267
+ array(
268
+ 'debug_logging_enabled' => array(
269
  'title' => __( 'Enable Logging', 'woocommerce-square' ),
270
  'type' => 'checkbox',
271
  'label' => sprintf(
272
  /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
273
  __( 'Log debug messages to the %1$sWooCommerce status log%2$s', 'woocommerce-square' ),
274
+ '<a href="' . esc_url( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) . '">',
275
+ '</a>'
276
  ),
277
+ ),
278
+ )
279
  );
280
 
281
  $this->form_fields = $fields;
295
 
296
  $fields = parent::get_form_fields();
297
 
298
+ // Confirm our local enable sandbox setting matches what is sent from the front end
299
+ // to account for changes from sandbox to production incorrectly fetching sandbox locations.
300
+ if ( isset( $_POST['wc_square_environment'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
301
+
302
+ $environment = 'yes' === $this->settings['enable_sandbox'] ? 'sandbox' : 'production';
303
+
304
+ if ( $environment !== $_POST['wc_square_environment'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
305
+ return $fields;
306
+ }
307
+ }
308
+
309
+ $location_id_field_key = '';
310
+ // Get the location_id field.
311
+ foreach ( $fields as $key => $value ) {
312
+ if ( strpos( $key, 'location_id' ) ) {
313
+ $location_id_field_key = $key;
314
+ break;
315
+ }
316
+ }
317
+
318
+ if ( did_action( 'wc_square_initialized' ) && $this->is_admin_settings_screen() && ! empty( $location_id_field_key ) ) {
319
+
320
+ $locations = array(
321
  '' => __( 'Please choose a location', 'woocommerce-square' ),
322
+ );
323
 
324
  if ( ! empty( $this->get_locations() ) ) {
 
325
  foreach ( $this->get_locations() as $location ) {
 
326
  if ( 'ACTIVE' === $location->getStatus() && in_array( 'CREDIT_CARD_PROCESSING', (array) $location->getCapabilities(), true ) ) {
327
  $locations[ $location->getId() ] = $location->getName();
328
  }
329
  }
330
  }
331
 
332
+ $fields[ $location_id_field_key ]['options'] = $locations;
 
333
  }
334
 
335
  return $fields;
336
  }
337
 
338
 
339
+ /**
340
+ * Generates the HTML for import products button.
341
+ *
342
+ * @param string $id form id.
343
+ * @param array $field form fields.
344
+ */
345
  public function generate_import_products_html( $id, $field ) {
346
 
347
  ob_start();
348
  ?>
349
  <tr valign="top">
350
  <th scope="row" class="titledesc">
351
+ <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
352
  </th>
353
  <td class="forminp">
354
  <a href='#' class='button js-import-square-products'>
367
  *
368
  * @since 2.0.0
369
  *
370
+ * @param string $id field ID.
371
+ * @param array $field field data.
372
  * @return string
373
  */
374
  public function generate_connect_html( $id, $field ) {
377
  ?>
378
  <tr valign="top">
379
  <th scope="row" class="titledesc">
380
+ <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
381
  </th>
382
  <td class="forminp">
383
+ <?php
384
+ if ( $this->get_access_token() ) {
385
+ echo $this->get_plugin()->get_connection_handler()->get_disconnect_button_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
386
  } else {
387
+ echo $this->get_plugin()->get_connection_handler()->get_connect_button_html( $this->is_sandbox() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
388
+ }
389
+ ?>
390
  </td>
391
  </tr>
392
  <?php
400
  *
401
  * @since 2.0.0
402
  *
403
+ * @param string $token refresh token.
404
  */
405
  public function update_refresh_token( $token ) {
406
 
407
  $refresh_tokens = $this->get_refresh_tokens();
408
+ $environment = $this->get_environment();
409
 
410
  if ( ! empty( $token ) ) {
411
 
421
 
422
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
423
 
424
+ // log the event, but don't halt the process.
425
  $this->get_plugin()->log( 'Could not encrypt refresh token. ' . $exception->getMessage() );
426
  }
427
  }
438
  *
439
  * @since 2.0.0
440
  *
441
+ * @param string $token access token.
442
  */
443
  public function update_access_token( $token ) {
444
 
459
 
460
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
461
 
462
+ // log the event, but don't halt the process.
463
  $this->get_plugin()->log( 'Could not encrypt access token. ' . $exception->getMessage() );
464
  }
465
  }
466
 
467
  $access_tokens[ $environment ] = $token;
468
+ } elseif ( isset( $access_tokens[ $environment ] ) ) {
469
+
470
+ unset( $access_tokens[ $environment ] );
471
  }
472
 
473
  update_option( 'wc_square_access_tokens', $access_tokens );
504
  */
505
  public function clear_location_id() {
506
 
507
+ $settings = get_option( $this->get_option_key(), array() );
508
 
509
+ $settings[ $this->get_environment() . '_location_id' ] = '';
510
 
511
  update_option( $this->get_option_key(), $settings );
512
  }
658
  * @return string
659
  */
660
  public function get_location_id() {
661
+ $location_id = $this->get_option( $this->get_environment() . '_location_id' );
662
+
663
+ if ( empty( $location_id ) ) {
664
+ $square_db_version = get_option( $this->get_plugin()->get_plugin_version_name() );
665
 
666
+ // if the Square DB version is still pre-2.2.0, fetch the location ID using the previous option name
667
+ if ( ! empty( $square_db_version ) && version_compare( $square_db_version, '2.2.0', '<' ) ) {
668
+ $location_id = $this->get_option( 'location_id' );
669
+ }
670
+ }
671
+
672
+ return $location_id;
673
  }
674
 
675
 
682
  */
683
  public function get_locations() {
684
 
 
685
  if ( is_array( $this->locations ) ) {
686
 
687
  return $this->locations;
688
  }
689
 
690
+ // don't always need to refetch when not on Settings screen.
691
  if ( ! $this->is_admin_settings_screen() ) {
692
 
693
  $this->locations = get_transient( 'wc_square_locations' );
695
 
696
  if ( ! is_array( $this->locations ) && did_action( 'wc_square_initialized' ) ) {
697
 
698
+ $this->locations = array();
699
 
700
  try {
701
 
702
+ // cache the locations returned so they can be used elsewhere.
703
  $this->locations = $this->get_plugin()->get_api( $this->get_access_token(), $this->is_sandbox() )->get_locations();
704
  set_transient( 'wc_square_locations', $this->locations, HOUR_IN_SECONDS );
705
 
706
+ // check the returned IDs against what's currently configured.
707
  $stored_location_id = $this->get_location_id();
708
  $found = ! $stored_location_id;
709
 
715
  }
716
  }
717
 
718
+ // if the currently set location ID is not present in the connected account's available locations, clear it locally.
719
  if ( ! $found ) {
720
  $this->clear_location_id();
721
  }
 
722
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
723
 
724
  $this->get_plugin()->log( 'Could not retrieve business locations.' );
753
 
754
  switch ( $this->get_system_of_record() ) {
755
 
756
+ case 'square':
757
  $sor = __( 'Square', 'woocommerce-square' );
758
+ break;
759
+ case 'woocommerce':
760
  $sor = __( 'WooCommerce', 'woocommerce-square' );
761
+ break;
762
+ default:
763
  $sor = '';
764
+ break;
765
  }
766
 
767
  return $sor;
795
 
796
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
797
 
798
+ // log the event, but don't halt the process.
799
  $this->get_plugin()->log( 'Could not decrypt refresh token. ' . $exception->getMessage() );
800
  }
801
  }
822
  */
823
  public function get_access_token() {
824
 
825
+ if ( empty( $this->access_token ) || $this->is_admin_settings_screen() ) {
826
 
827
  $tokens = $this->get_access_tokens();
828
  $token = null;
841
 
842
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
843
 
844
+ // log the event, but don't halt the process.
845
  $this->get_plugin()->log( 'Could not decrypt access token. ' . $exception->getMessage() );
846
  }
847
  }
870
  * @return array
871
  */
872
  public function get_access_tokens() {
873
+ return (array) get_option( 'wc_square_access_tokens', array() );
874
  }
875
 
876
 
884
  * @return array
885
  */
886
  public function get_refresh_tokens() {
887
+ return (array) get_option( 'wc_square_refresh_tokens', array() );
888
  }
889
 
890
  /**
906
  * @return boolean
907
  */
908
  public function is_sandbox_setting_enabled() {
909
+ return 'yes' === $this->get_enable_sandbox();
 
910
  }
911
 
912
 
942
  * @return bool True if the current request is for the Square admin settings, otherwise false.
943
  */
944
  public function is_admin_settings_screen() {
945
+ return isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && Plugin::PLUGIN_ID === $_GET['tab']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
946
  }
947
  }
includes/Sync/Catalog_Item.php CHANGED
@@ -26,7 +26,7 @@ namespace WooCommerce\Square\Sync;
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\Handlers\Product;
28
 
29
- defined( 'ABSPATH' ) or exit;
30
 
31
  /**
32
  * Class to represent a single catalog item.
@@ -128,19 +128,21 @@ class Catalog_Item {
128
 
129
  if ( ! $catalog_object ) {
130
 
131
- $catalog_object = new \SquareConnect\Model\CatalogObject( [
132
- 'type' => 'ITEM',
133
- ] );
 
 
134
  }
135
 
136
  // update the object data from the Woo product
137
  $catalog_object = Product\Woo_SOR::update_catalog_item( $catalog_object, $this->product );
138
 
139
- $batch_data = [ 'objects' => [ $catalog_object ] ];
140
 
141
  $this->batch = new \SquareConnect\Model\CatalogObjectBatch( $batch_data );
142
 
143
- $variations = $catalog_object->getItemData()->getVariations() ?: [];
144
 
145
  $this->batch_object_count = 1 + count( $variations );
146
  }
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use WooCommerce\Square\Handlers\Product;
28
 
29
+ defined( 'ABSPATH' ) || exit;
30
 
31
  /**
32
  * Class to represent a single catalog item.
128
 
129
  if ( ! $catalog_object ) {
130
 
131
+ $catalog_object = new \SquareConnect\Model\CatalogObject(
132
+ array(
133
+ 'type' => 'ITEM',
134
+ )
135
+ );
136
  }
137
 
138
  // update the object data from the Woo product
139
  $catalog_object = Product\Woo_SOR::update_catalog_item( $catalog_object, $this->product );
140
 
141
+ $batch_data = array( 'objects' => array( $catalog_object ) );
142
 
143
  $this->batch = new \SquareConnect\Model\CatalogObjectBatch( $batch_data );
144
 
145
+ $variations = $catalog_object->getItemData()->getVariations() ?: array();
146
 
147
  $this->batch_object_count = 1 + count( $variations );
148
  }
includes/Sync/Interval_Polling.php CHANGED
@@ -25,10 +25,11 @@ namespace WooCommerce\Square\Sync;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use SquareConnect\Model\SearchCatalogObjectsResponse;
 
28
  use WooCommerce\Square\Handlers\Product;
29
  use WooCommerce\Square\Handlers\Category;
30
 
31
- defined( 'ABSPATH' ) or exit;
32
 
33
  /**
34
  * Class to represent a synchronization job to poll latest product updates at intervals.
@@ -47,14 +48,14 @@ class Interval_Polling extends Stepped_Job {
47
  */
48
  protected function assign_next_steps() {
49
 
50
- $next_steps = [];
51
 
52
  if ( $this->is_system_of_record_square() ) {
53
 
54
- $next_steps = [
55
  'update_category_data',
56
  'update_product_data',
57
- ];
58
  }
59
 
60
  // only pull latest inventory if enabled
@@ -77,10 +78,12 @@ class Interval_Polling extends Stepped_Job {
77
  $date->setTimestamp( $this->get_attr( 'catalog_last_synced_at', (int) wc_square()->get_sync_handler()->get_last_synced_at() ) );
78
  $date->setTimezone( new \DateTimeZone( 'UTC' ) );
79
 
80
- $response = wc_square()->get_api()->search_catalog_objects( [
81
- 'object_types' => [ 'CATEGORY' ],
82
- 'begin_time' => $date->format( DATE_ATOM ),
83
- ] );
 
 
84
 
85
  $categories = $response->get_data()->getObjects();
86
 
@@ -89,14 +92,16 @@ class Interval_Polling extends Stepped_Job {
89
  Category::import_or_update( $category );
90
  }
91
 
92
- Records::set_record( [
93
- 'type' => 'info',
94
- 'message' => sprintf(
95
- /* translator: Placeholder %d number of categories */
96
- _n( 'Updated data for %d category.', 'Updated data for %d categories.', count( $categories ), 'woocommerce-square' ),
97
- count( $categories )
 
 
98
  )
99
- ] );
100
  }
101
 
102
  $this->complete_step( 'update_category_data' );
@@ -115,15 +120,17 @@ class Interval_Polling extends Stepped_Job {
115
  $date->setTimestamp( $this->get_attr( 'catalog_last_synced_at', (int) wc_square()->get_sync_handler()->get_last_synced_at() ) );
116
  $date->setTimezone( new \DateTimeZone( 'UTC' ) );
117
 
118
- $products_updated = $this->get_attr( 'processed_product_ids', [] );
119
  $cursor = $this->get_attr( 'update_product_data_cursor' );
120
 
121
- $response = wc_square()->get_api()->search_catalog_objects( [
122
- 'object_types' => [ 'ITEM' ],
123
- 'include_deleted_objects' => true,
124
- 'begin_time' => $date->format( DATE_ATOM ),
125
- 'cursor' => $cursor,
126
- ] );
 
 
127
 
128
  // store the timestamp after this API request was completed
129
  // we don't want to set it at the end, as counts may have changed in the time it takes to process the data
@@ -147,10 +154,10 @@ class Interval_Polling extends Stepped_Job {
147
  // deleted items won't have any data to set, so don't try and update the product
148
  if ( $object->getIsDeleted() ) {
149
 
150
- $record = [
151
  'type' => 'alert',
152
  'product_id' => $product->get_id(),
153
- ];
154
 
155
  // if enabled, hide the product from the catalog
156
  if ( wc_square()->get_settings_handler()->hide_missing_square_products() ) {
@@ -162,7 +169,8 @@ class Interval_Polling extends Stepped_Job {
162
 
163
  $record['product_hidden'] = true;
164
 
165
- } catch ( \Exception $e ) {}
 
166
  }
167
 
168
  Records::set_record( $record );
@@ -181,17 +189,19 @@ class Interval_Polling extends Stepped_Job {
181
 
182
  } catch ( \Exception $exception ) {
183
 
184
- Records::set_record( [
185
- 'type' => 'alert',
186
- 'product_id' => $product->get_id(),
187
- ] );
 
 
188
  }
189
  }
190
  }
191
  }
192
  }
193
 
194
- $cursor = $response->get_data()->getCursor();
195
 
196
  $this->set_attr( 'update_product_data_cursor', $cursor );
197
  $this->set_attr( 'processed_product_ids', array_unique( $products_updated ) );
@@ -216,10 +226,10 @@ class Interval_Polling extends Stepped_Job {
216
  $products_updated = $this->get_attr( 'processed_product_ids' );
217
  $cursor = $this->get_attr( 'update_inventory_counts_cursor' );
218
 
219
- $args = [
220
- 'location_ids' => [ wc_square()->get_settings_handler()->get_location_id() ],
221
  'cursor' => $cursor,
222
- ];
223
 
224
  $last_synced_at = $this->get_attr( 'inventory_last_synced_at' );
225
 
@@ -258,7 +268,7 @@ class Interval_Polling extends Stepped_Job {
258
  }
259
  }
260
 
261
- $cursor = $response->get_data()->getCursor();
262
 
263
  $this->set_attr( 'update_inventory_counts_cursor', $cursor );
264
  $this->set_attr( 'processed_product_ids', array_unique( $products_updated ) );
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
  use SquareConnect\Model\SearchCatalogObjectsResponse;
28
+ use SquareConnect\Model\BatchRetrieveInventoryCountsResponse;
29
  use WooCommerce\Square\Handlers\Product;
30
  use WooCommerce\Square\Handlers\Category;
31
 
32
+ defined( 'ABSPATH' ) || exit;
33
 
34
  /**
35
  * Class to represent a synchronization job to poll latest product updates at intervals.
48
  */
49
  protected function assign_next_steps() {
50
 
51
+ $next_steps = array();
52
 
53
  if ( $this->is_system_of_record_square() ) {
54
 
55
+ $next_steps = array(
56
  'update_category_data',
57
  'update_product_data',
58
+ );
59
  }
60
 
61
  // only pull latest inventory if enabled
78
  $date->setTimestamp( $this->get_attr( 'catalog_last_synced_at', (int) wc_square()->get_sync_handler()->get_last_synced_at() ) );
79
  $date->setTimezone( new \DateTimeZone( 'UTC' ) );
80
 
81
+ $response = wc_square()->get_api()->search_catalog_objects(
82
+ array(
83
+ 'object_types' => array( 'CATEGORY' ),
84
+ 'begin_time' => $date->format( DATE_ATOM ),
85
+ )
86
+ );
87
 
88
  $categories = $response->get_data()->getObjects();
89
 
92
  Category::import_or_update( $category );
93
  }
94
 
95
+ Records::set_record(
96
+ array(
97
+ 'type' => 'info',
98
+ 'message' => sprintf(
99
+ /* translator: Placeholder %d number of categories */
100
+ _n( 'Updated data for %d category.', 'Updated data for %d categories.', count( $categories ), 'woocommerce-square' ),
101
+ count( $categories )
102
+ ),
103
  )
104
+ );
105
  }
106
 
107
  $this->complete_step( 'update_category_data' );
120
  $date->setTimestamp( $this->get_attr( 'catalog_last_synced_at', (int) wc_square()->get_sync_handler()->get_last_synced_at() ) );
121
  $date->setTimezone( new \DateTimeZone( 'UTC' ) );
122
 
123
+ $products_updated = $this->get_attr( 'processed_product_ids', array() );
124
  $cursor = $this->get_attr( 'update_product_data_cursor' );
125
 
126
+ $response = wc_square()->get_api()->search_catalog_objects(
127
+ array(
128
+ 'object_types' => array( 'ITEM' ),
129
+ 'include_deleted_objects' => true,
130
+ 'begin_time' => $date->format( DATE_ATOM ),
131
+ 'cursor' => $cursor,
132
+ )
133
+ );
134
 
135
  // store the timestamp after this API request was completed
136
  // we don't want to set it at the end, as counts may have changed in the time it takes to process the data
154
  // deleted items won't have any data to set, so don't try and update the product
155
  if ( $object->getIsDeleted() ) {
156
 
157
+ $record = array(
158
  'type' => 'alert',
159
  'product_id' => $product->get_id(),
160
+ );
161
 
162
  // if enabled, hide the product from the catalog
163
  if ( wc_square()->get_settings_handler()->hide_missing_square_products() ) {
169
 
170
  $record['product_hidden'] = true;
171
 
172
+ } catch ( \Exception $e ) {
173
+ }
174
  }
175
 
176
  Records::set_record( $record );
189
 
190
  } catch ( \Exception $exception ) {
191
 
192
+ Records::set_record(
193
+ array(
194
+ 'type' => 'alert',
195
+ 'product_id' => $product->get_id(),
196
+ )
197
+ );
198
  }
199
  }
200
  }
201
  }
202
  }
203
 
204
+ $cursor = $response->get_data() instanceof SearchCatalogObjectsResponse ? $response->get_data()->getCursor() : null;
205
 
206
  $this->set_attr( 'update_product_data_cursor', $cursor );
207
  $this->set_attr( 'processed_product_ids', array_unique( $products_updated ) );
226
  $products_updated = $this->get_attr( 'processed_product_ids' );
227
  $cursor = $this->get_attr( 'update_inventory_counts_cursor' );
228
 
229
+ $args = array(
230
+ 'location_ids' => array( wc_square()->get_settings_handler()->get_location_id() ),
231
  'cursor' => $cursor,
232
+ );
233
 
234
  $last_synced_at = $this->get_attr( 'inventory_last_synced_at' );
235
 
268
  }
269
  }
270
 
271
+ $cursor = $response->get_data() instanceof BatchRetrieveInventoryCountsResponse ? $response->get_data()->getCursor() : null;
272
 
273
  $this->set_attr( 'update_inventory_counts_cursor', $cursor );
274
  $this->set_attr( 'processed_product_ids', array_unique( $products_updated ) );
includes/Sync/Job.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\Sync;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Synchronization Job abstract.
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Synchronization Job abstract.
includes/Sync/Manual_Synchronization.php CHANGED
@@ -33,7 +33,7 @@ use SquareConnect\ObjectSerializer;
33
  use WooCommerce\Square\Handlers\Category;
34
  use WooCommerce\Square\Handlers\Product;
35
 
36
- defined( 'ABSPATH' ) or exit;
37
 
38
  /**
39
  * Class to represent a single synchronization job triggered manually.
@@ -49,6 +49,10 @@ class Manual_Synchronization extends Stepped_Job {
49
  /** @var int the limit for how many inventory changes can be made in a single request */
50
  const BATCH_CHANGE_INVENTORY_LIMIT = 100;
51
 
 
 
 
 
52
 
53
  /**
54
  * Validates the products attached to this job.
@@ -59,22 +63,20 @@ class Manual_Synchronization extends Stepped_Job {
59
 
60
  $product_ids = $this->get_attr( 'product_ids' );
61
 
62
- $products_query = [
63
  'include' => $product_ids,
64
  'limit' => -1,
65
- 'return' => 'ids'
66
- ];
67
-
68
- $validated_products = wc_get_products( $products_query );
69
 
70
  if ( 'delete' === $this->get_attr( 'action' ) ) {
71
 
72
- $products_query['status'] = 'trash';
73
- $trashed_products = wc_get_products( $products_query );
74
-
75
- $validated_products = array_unique( array_merge( $validated_products, $trashed_products ), SORT_NUMERIC );
76
  }
77
 
 
 
78
  $this->set_attr( 'validated_product_ids', $validated_products );
79
 
80
  $this->complete_step( 'validate_products' );
@@ -97,11 +99,11 @@ class Manual_Synchronization extends Stepped_Job {
97
  $limits = $catalog_info->get_data()->getLimits();
98
 
99
  $this->set_attr( 'max_objects_to_retrieve', $limits->getBatchRetrieveMaxObjectIds() );
100
- $this->set_attr( 'max_objects_per_batch', $limits->getBatchUpsertMaxObjectsPerBatch() );
101
- $this->set_attr( 'max_objects_total', $limits->getBatchUpsertMaxTotalObjects() );
102
  }
103
-
104
- } catch ( Framework\SV_WC_Plugin_Exception $exception ) {} // no need to handle errors here
105
 
106
  $this->complete_step( 'update_limits' );
107
  }
@@ -133,9 +135,9 @@ class Manual_Synchronization extends Stepped_Job {
133
 
134
  $map = Category::get_map();
135
  $category_ids = $this->get_attr( 'refresh_mappings_category_ids', $this->get_attr( 'category_ids' ) );
136
- $mapped_categories = [];
137
- $unmapped_categories = $this->get_attr( 'unmapped_categories', [] );
138
- $unmapped_category_ids = [];
139
 
140
  if ( empty( $category_ids ) ) {
141
  $this->complete_step( 'refresh_category_mappings' );
@@ -152,7 +154,7 @@ class Manual_Synchronization extends Stepped_Job {
152
 
153
  } else {
154
 
155
- $this->set_attr( 'refresh_mappings_category_ids', [] );
156
  }
157
 
158
  foreach ( $category_ids as $category_id ) {
@@ -169,14 +171,21 @@ class Manual_Synchronization extends Stepped_Job {
169
 
170
  if ( ! empty( $mapped_categories ) ) {
171
 
172
- $square_ids = array_values( array_filter( array_map( function ( $mapped_category ) {
173
- return isset( $mapped_category['square_id'] ) ? $mapped_category['square_id'] : null;
174
- }, $mapped_categories ) ) );
 
 
 
 
 
 
 
175
 
176
  $response = wc_square()->get_api()->batch_retrieve_catalog_objects( $square_ids );
177
 
178
  // swap the square ID into the array key for quick lookup
179
- $mapped_category_audit = [];
180
 
181
  foreach ( $mapped_categories as $mapped_category_id => $mapped_category ) {
182
  $mapped_category_audit[ $mapped_category['square_id'] ] = $mapped_category_id;
@@ -218,10 +227,12 @@ class Manual_Synchronization extends Stepped_Job {
218
 
219
  if ( ! empty( $unmapped_category_ids ) ) {
220
 
221
- $unmapped_category_terms = get_terms( [
222
- 'taxonomy' => 'product_cat',
223
- 'include' => $unmapped_category_ids,
224
- ] );
 
 
225
 
226
  // make the 'name' attribute the array key, for more efficient searching later.
227
  foreach ( $unmapped_category_terms as $unmapped_category_term ) {
@@ -246,8 +257,8 @@ class Manual_Synchronization extends Stepped_Job {
246
  */
247
  protected function query_unmapped_categories() {
248
 
249
- $unmapped_categories = $this->get_attr( 'unmapped_categories', [] );
250
- $mapped_categories = $this->get_attr( 'mapped_categories', [] );
251
 
252
  if ( empty( $unmapped_categories ) ) {
253
 
@@ -255,10 +266,12 @@ class Manual_Synchronization extends Stepped_Job {
255
 
256
  } else {
257
 
258
- $response = wc_square()->get_api()->search_catalog_objects( [
259
- 'object_types' => [ 'CATEGORY' ],
260
- 'cursor' => $this->get_attr( 'unmapped_categories_cursor' ),
261
- ] );
 
 
262
 
263
  $category_map = Category::get_map();
264
  $categories = $response->get_data()->getObjects();
@@ -273,10 +286,10 @@ class Manual_Synchronization extends Stepped_Job {
273
 
274
  $category_id = $unmapped_categories[ $unmapped_category_key ]['term_id'];
275
 
276
- $category_map[ $category_id ] = [
277
  'square_id' => $category_object->getId(),
278
  'square_version' => $category_object->getVersion(),
279
- ];
280
 
281
  $mapped_categories[] = $category_id;
282
  unset( $unmapped_categories[ $unmapped_category_key ] );
@@ -309,13 +322,15 @@ class Manual_Synchronization extends Stepped_Job {
309
  protected function upsert_categories() {
310
 
311
  $category_ids = $this->get_attr( 'category_ids' );
312
- $categories = get_terms( [
313
- 'taxonomy' => 'product_cat',
314
- 'include' => $category_ids,
315
- ] );
 
 
316
 
317
- $batches = [];
318
- $reverse_map = [];
319
 
320
  // For now, keep it to one category per batch. Since we can still send 1000 batches per request, it's efficient,
321
  // and insulates errors per category rather than a single category error breaking the entire batch it is in.
@@ -329,19 +344,19 @@ class Manual_Synchronization extends Stepped_Job {
329
 
330
  $reverse_map[ $square_id ] = $category_id;
331
 
332
- $catalog_object_data = [
333
  'type' => 'CATEGORY',
334
  'id' => $square_id,
335
- 'category_data' => [
336
  'name' => wp_specialchars_decode( $category->name ), // names are stored encoded in the database
337
- ]
338
- ];
339
 
340
  if ( 0 < $square_version ) {
341
  $catalog_object_data['version'] = $square_version;
342
  }
343
 
344
- $batches[] = new \SquareConnect\Model\CatalogObjectBatch( [ 'objects' => [ new \SquareConnect\Model\CatalogObject( $catalog_object_data ) ] ] );
345
  }
346
 
347
  $idempotency_key = wc_square()->get_idempotency_key( md5( serialize( $batches ) . $this->get_attr( 'id' ) ) . '_upsert_categories' );
@@ -389,8 +404,8 @@ class Manual_Synchronization extends Stepped_Job {
389
  */
390
  protected function prepare_matched_products_for_upsert() {
391
 
392
- $product_ids_to_prepare = $this->get_attr( 'product_ids_to_prepare', $this->get_attr( 'validated_product_ids', [] ) );
393
- $in_progress = $this->get_attr( 'in_progress_prepare_matched_products_for_upsert', [] );
394
 
395
  if ( empty( $product_ids_to_prepare ) ) {
396
  $this->complete_step( 'prepare_matched_products_for_upsert' );
@@ -429,7 +444,7 @@ class Manual_Synchronization extends Stepped_Job {
429
 
430
  try {
431
 
432
- $objects = [];
433
  $api_response = json_decode( $in_progress['api_response'], true );
434
 
435
  if ( isset( $api_response['objects'] ) ) {
@@ -438,9 +453,10 @@ class Manual_Synchronization extends Stepped_Job {
438
  }
439
  }
440
 
441
- $response = new \SquareConnect\Model\BatchRetrieveCatalogObjectsResponse( [ 'objects' => $objects ] );
442
 
443
- } catch ( \Exception $e ) {}
 
444
  }
445
 
446
  if ( null === $response ) {
@@ -460,7 +476,7 @@ class Manual_Synchronization extends Stepped_Job {
460
  return;
461
  }
462
 
463
- $catalog_objects = isset( $in_progress['catalog_objects'] ) ? $in_progress['catalog_objects'] : [];
464
 
465
  if ( $response && $response_objects = $response->getObjects() ) {
466
 
@@ -487,11 +503,11 @@ class Manual_Synchronization extends Stepped_Job {
487
  }
488
  }
489
 
490
- $matched_products_to_upsert = $this->get_attr( 'matched_products_to_upsert', [] );
491
 
492
  $this->set_attr( 'matched_products_to_upsert', $matched_products_to_upsert + $catalog_objects );
493
  $this->set_attr( 'product_ids_to_prepare', array_diff( $product_ids_to_prepare, $product_ids ) );
494
- $this->set_attr( 'in_progress_prepare_matched_products_for_upsert', [] );
495
  }
496
 
497
 
@@ -504,7 +520,7 @@ class Manual_Synchronization extends Stepped_Job {
504
  */
505
  protected function upsert_matched_products() {
506
 
507
- $matched_products_to_upsert = $this->get_attr( 'matched_products_to_upsert', [] );
508
 
509
  if ( empty( $matched_products_to_upsert ) ) {
510
 
@@ -517,7 +533,7 @@ class Manual_Synchronization extends Stepped_Job {
517
 
518
  if ( isset( $result['processed'] ) ) {
519
 
520
- $processed_product_ids = $this->get_attr( 'processed_product_ids', [] );
521
 
522
  $this->set_attr( 'processed_product_ids', array_merge( $processed_product_ids, $result['processed'] ) );
523
 
@@ -536,8 +552,8 @@ class Manual_Synchronization extends Stepped_Job {
536
  */
537
  protected function update_matched_products() {
538
 
539
- $product_ids = $this->get_attr( 'matched_product_ids', $this->get_attr( 'validated_product_ids', [] ) );
540
- $processed_product_ids = $this->get_attr( 'processed_product_ids', [] );
541
 
542
  // remove IDs that have already been processed
543
  $product_ids = array_diff( $product_ids, $processed_product_ids );
@@ -558,7 +574,7 @@ class Manual_Synchronization extends Stepped_Job {
558
 
559
  } else {
560
 
561
- $this->set_attr( 'matched_product_ids', [] );
562
  }
563
 
564
  $products_map = Product::get_square_meta( $product_ids, 'square_item_id' );
@@ -574,7 +590,7 @@ class Manual_Synchronization extends Stepped_Job {
574
  throw new Framework\SV_WC_API_Exception( 'Response data is missing' );
575
  }
576
 
577
- $catalog_objects = [];
578
 
579
  if ( $response->get_data()->getObjects() ) {
580
 
@@ -596,7 +612,7 @@ class Manual_Synchronization extends Stepped_Job {
596
  $this->set_attr( 'processed_product_ids', array_merge( $result['processed'], $processed_product_ids ) );
597
 
598
  // any products that were staged but not processed, push to the matched array to try next time
599
- $matched_product_ids = $this->get_attr( 'matched_product_ids', [] );
600
  $this->set_attr( 'matched_product_ids', array_merge( $result['unprocessed'], $matched_product_ids ) );
601
  }
602
  }
@@ -611,14 +627,17 @@ class Manual_Synchronization extends Stepped_Job {
611
  */
612
  protected function search_matched_products() {
613
 
614
- $product_ids = $this->get_attr( 'search_product_ids', $this->get_attr( 'validated_product_ids', [] ) );
615
- $processed_product_ids = $this->get_attr( 'processed_product_ids', [] );
616
- $in_progress = $this->get_attr( 'in_progress_search_matched_products', [
617
- 'unprocessed_search_response' => null,
618
- 'processed_remote_object_ids' => [],
619
- 'catalog_objects_to_update' => [],
620
- 'upserting' => false,
621
- ] );
 
 
 
622
 
623
  // remove IDs that have already been processed
624
  $product_ids = array_diff( $product_ids, $processed_product_ids );
@@ -643,11 +662,13 @@ class Manual_Synchronization extends Stepped_Job {
643
 
644
  } else {
645
 
646
- $response = wc_square()->get_api()->search_catalog_objects( [
647
- 'cursor' => $this->get_attr( 'search_products_cursor' ),
648
- 'object_types' => [ 'ITEM' ],
649
- 'limit' => $this->get_max_objects_to_retrieve(),
650
- ] );
 
 
651
 
652
  $search_response = $response->get_data();
653
 
@@ -658,7 +679,7 @@ class Manual_Synchronization extends Stepped_Job {
658
  throw new Framework\SV_WC_API_Exception( 'Response data is missing' );
659
  }
660
 
661
- $catalog_objects = $search_response->getObjects() ?: [];
662
  $cursor = $search_response->getCursor();
663
  $catalog_objects_to_update = $in_progress['catalog_objects_to_update'];
664
 
@@ -764,8 +785,8 @@ class Manual_Synchronization extends Stepped_Job {
764
 
765
  if ( ! empty( $result['unprocessed'] ) ) {
766
 
767
- $catalog_processed = false;
768
- $remaining_product_ids = array_merge( $result['unprocessed'], $remaining_product_ids );
769
  $in_progress['catalog_objects_to_update'] = array_diff_key( $catalog_objects_to_update, array_flip( $processed_product_ids ) );
770
 
771
  } else {
@@ -779,7 +800,7 @@ class Manual_Synchronization extends Stepped_Job {
779
  if ( ! $catalog_processed && ! empty( $remaining_product_ids ) ) {
780
 
781
  $this->set_attr( 'search_products_cursor', $cursor );
782
- $this->set_attr( 'search_product_ids', $remaining_product_ids );
783
 
784
  } else {
785
 
@@ -794,8 +815,8 @@ class Manual_Synchronization extends Stepped_Job {
794
  */
795
  protected function upsert_new_products() {
796
 
797
- $product_ids = $this->get_attr( 'upsert_new_product_ids', $this->get_attr( 'validated_product_ids', [] ) );
798
- $processed_product_ids = $this->get_attr( 'processed_product_ids', [] );
799
 
800
  // remove IDs that have already been processed
801
  $product_ids = array_diff( $product_ids, $processed_product_ids );
@@ -806,14 +827,16 @@ class Manual_Synchronization extends Stepped_Job {
806
  return;
807
  }
808
 
809
- $catalog_objects = [];
810
 
811
  foreach ( $product_ids as $product_id ) {
812
 
813
- $catalog_objects[ $product_id ] = new CatalogObject( [
814
- 'type' => 'ITEM',
815
- 'item_data' => new \SquareConnect\Model\CatalogItem(),
816
- ] );
 
 
817
  }
818
 
819
  $result = $this->upsert_catalog_objects( $catalog_objects );
@@ -856,24 +879,27 @@ class Manual_Synchronization extends Stepped_Job {
856
 
857
  $is_delete_action = 'delete' === $this->get_attr( 'action' );
858
  $product_ids = array_keys( $objects );
859
- $original_square_image_ids = [];
860
- $staged_product_ids = [];
861
- $successful_product_ids = [];
862
  $total_object_count = 0;
863
- $batches = [];
864
- $result = [
865
- 'processed' => [],
866
  'unprocessed' => $product_ids,
867
- ];
868
-
869
- $in_progress = $this->get_attr( 'in_progress_upsert_catalog_objects', [
870
- 'batches' => [],
871
- 'staged_product_ids' => [],
872
- 'total_object_count' => null,
873
- 'unprocessed_upsert_response' => null,
874
- 'mapped_client_item_ids' => [],
875
- 'processed_remote_catalog_item_ids' => []
876
- ] );
 
 
 
877
 
878
  if ( null === $in_progress['unprocessed_upsert_response'] ) {
879
 
@@ -882,9 +908,12 @@ class Manual_Synchronization extends Stepped_Job {
882
 
883
  $staged_product_ids = $in_progress['staged_product_ids'];
884
  $total_object_count = $in_progress['total_object_count'];
885
- $batches = array_map( static function ( $batch_data ) {
886
- return ObjectSerializer::deserialize( json_decode( $batch_data, false ), CatalogObjectBatch::class );
887
- }, $in_progress['batches'] );
 
 
 
888
  }
889
 
890
  foreach ( $objects as $product_id => $object ) {
@@ -909,7 +938,7 @@ class Manual_Synchronization extends Stepped_Job {
909
  $object = $this->convert_to_catalog_object( $object );
910
  }
911
 
912
- $product = wc_get_product( $product_id );
913
  $original_square_image_ids[ $product_id ] = $product->get_meta( '_square_item_image_id' );
914
 
915
  $catalog_item = new Catalog_Item( $product, $is_delete_action );
@@ -918,7 +947,7 @@ class Manual_Synchronization extends Stepped_Job {
918
 
919
  if ( $this->get_max_objects_total() >= $object_count + $total_object_count ) {
920
  $batches[] = $batch;
921
- $total_object_count += $object_count;
922
  $staged_product_ids[] = $product_id;
923
  } else {
924
  break;
@@ -1002,7 +1031,7 @@ class Manual_Synchronization extends Stepped_Job {
1002
  wc_square()->log( 'Mapped ' . count( $in_progress['mapped_client_item_ids'] ) . ' Square IDs in ' . $duration . 's' );
1003
  }
1004
 
1005
- $pull_inventory_variation_ids = $this->get_attr( 'pull_inventory_variation_ids', [] );
1006
 
1007
  wc_square()->log( 'Storing Square item data to WooCommerce products' );
1008
 
@@ -1032,11 +1061,14 @@ class Manual_Synchronization extends Stepped_Job {
1032
  continue;
1033
  }
1034
 
1035
- Product::update_square_meta( $product, [
1036
- 'item_id' => $remote_item_id,
1037
- 'item_version' => $remote_catalog_item->getVersion(),
1038
- 'item_image_id' => $remote_catalog_item->getImageId(),
1039
- ] );
 
 
 
1040
 
1041
  $successful_product_ids[] = $product->get_id();
1042
 
@@ -1048,10 +1080,13 @@ class Manual_Synchronization extends Stepped_Job {
1048
 
1049
  $pull_inventory_variation_ids[] = $catalog_item_variation->getId();
1050
 
1051
- Product::update_square_meta( $product_variation, [
1052
- 'item_variation_id' => $catalog_item_variation->getId(),
1053
- 'item_variation_version' => $catalog_item_variation->getVersion(),
1054
- ] );
 
 
 
1055
  }
1056
  }
1057
  }
@@ -1069,7 +1104,7 @@ class Manual_Synchronization extends Stepped_Job {
1069
 
1070
  // If there is a local image which is different from the last uploaded image
1071
  // Or if the remote square image id has changed
1072
- if ( ( $local_image_id && $local_image_id !== $product->get_meta( '_square_uploaded_image_id' ) ) ||
1073
  ( ! ( $original_square_image_ids[ $product_id ] && $original_square_image_ids[ $product_id ] === $product->get_meta( '_square_item_image_id' ) ) ) ) {
1074
  // there is no batch image endpoint
1075
  $this->push_product_image( $product );
@@ -1091,15 +1126,17 @@ class Manual_Synchronization extends Stepped_Job {
1091
  // log any failed products
1092
  foreach ( array_diff( $staged_product_ids, $successful_product_ids ) as $product_id ) {
1093
 
1094
- Records::set_record( [
1095
- 'type' => 'alert',
1096
- 'product_id' => $product_id,
1097
- 'message' => sprintf(
1098
- /* translators: Placeholder: %s - product ID */
1099
- esc_html__( 'Product %s could not be updated in Square.', 'woocommerce-square' ),
1100
- '<a href="' . esc_url( get_edit_post_link( $product_id ) ) . '">' . $product_id . '</a>'
1101
- ),
1102
- ] );
 
 
1103
  }
1104
 
1105
  $this->set_attr( 'in_progress_upsert_catalog_objects', null );
@@ -1179,8 +1216,8 @@ class Manual_Synchronization extends Stepped_Job {
1179
  */
1180
  protected function push_inventory() {
1181
 
1182
- $product_ids = $this->get_attr( 'inventory_push_product_ids', [] );
1183
- $inventory_changes = [];
1184
  $inventory_change_count = 0;
1185
 
1186
  foreach ( $product_ids as $key => $product_id ) {
@@ -1189,7 +1226,7 @@ class Manual_Synchronization extends Stepped_Job {
1189
 
1190
  if ( $product instanceof \WC_Product ) {
1191
 
1192
- $product_inventory_changes = [];
1193
 
1194
  if ( $product->is_type( 'variable' ) && $product->has_child() ) {
1195
 
@@ -1202,7 +1239,6 @@ class Manual_Synchronization extends Stepped_Job {
1202
  $product_inventory_changes[] = $inventory_change;
1203
  }
1204
  }
1205
-
1206
  } elseif ( $square_variation_id = Product::get_square_item_variation_id( $product_id, false ) ) {
1207
 
1208
  if ( $inventory_change = Product::get_inventory_change_physical_count_type( $product ) ) {
@@ -1221,7 +1257,6 @@ class Manual_Synchronization extends Stepped_Job {
1221
 
1222
  break;
1223
  }
1224
-
1225
  } else {
1226
 
1227
  unset( $product_ids[ $key ] );
@@ -1251,8 +1286,8 @@ class Manual_Synchronization extends Stepped_Job {
1251
  */
1252
  protected function square_sor_sync() {
1253
 
1254
- $synced_product_ids = $this->get_attr( 'validated_product_ids', [] );
1255
- $processed_product_ids = $this->get_attr( 'processed_product_ids', [] );
1256
  $unprocessed_product_ids = array_diff( $synced_product_ids, $processed_product_ids );
1257
  $catalog_processed = $this->get_attr( 'catalog_processed', false );
1258
 
@@ -1278,12 +1313,14 @@ class Manual_Synchronization extends Stepped_Job {
1278
 
1279
  $cursor = $this->get_attr( 'square_sor_cursor' );
1280
 
1281
- $response = wc_square()->get_api()->search_catalog_objects( [
1282
- 'cursor' => $cursor,
1283
- 'object_types' => [ 'ITEM' ],
1284
- 'include_related_objects' => true,
1285
- 'limit' => $this->get_max_objects_to_retrieve(),
1286
- ] );
 
 
1287
 
1288
  $response_data = $response->get_data();
1289
 
@@ -1304,14 +1341,13 @@ class Manual_Synchronization extends Stepped_Job {
1304
  $catalog_processed = ! $cursor;
1305
  $this->set_attr( 'catalog_processed', $catalog_processed );
1306
 
1307
- // bail early and fail for any API and plugin errors
1308
- } catch ( \Exception $exception ) {
1309
 
1310
  $this->fail( 'Product sync failed. ' . $exception->getMessage() );
1311
  return;
1312
  }
1313
 
1314
- $related_objects = $response->get_data()->getRelatedObjects();
1315
 
1316
  if ( $related_objects && is_array( $related_objects ) ) {
1317
  // first import any related categories
@@ -1322,10 +1358,10 @@ class Manual_Synchronization extends Stepped_Job {
1322
  }
1323
  }
1324
 
1325
- $pull_inventory_variation_ids = $this->get_attr( 'pull_inventory_variation_ids', [] );
1326
 
1327
  /** @var \SquareConnect\Model\CatalogObject[] */
1328
- $catalog_objects = $products_to_update = [];
1329
 
1330
  wc_square()->log( 'Searching for products in ' . count( $response_data->getObjects() ) . ' Square objects' );
1331
 
@@ -1426,10 +1462,9 @@ class Manual_Synchronization extends Stepped_Job {
1426
  if ( ! $product->get_image_id() && $square_object->getImageId() ) {
1427
  Product::update_image_from_square( $product, $square_object->getImageId() );
1428
  }
1429
-
1430
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
1431
 
1432
- $this->mark_failed_products( [ $product ] );
1433
  }
1434
 
1435
  $processed_product_ids[] = $product->get_id();
@@ -1452,12 +1487,18 @@ class Manual_Synchronization extends Stepped_Job {
1452
  */
1453
  protected function pull_inventory() {
1454
 
1455
- $processed_ids = $this->get_attr( 'processed_square_variation_ids', [] );
1456
 
1457
- $in_progress = wp_parse_args( $this->get_attr( 'in_progress_pull_inventory', [] ), [
1458
- 'response_data' => null,
1459
- 'processed_variation_ids' => [],
1460
- ] );
 
 
 
 
 
 
1461
 
1462
  $response_data = null;
1463
 
@@ -1469,7 +1510,7 @@ class Manual_Synchronization extends Stepped_Job {
1469
  // if the saved response was somehow corrupted, start over
1470
  if ( ! $response_data instanceof BatchRetrieveInventoryCountsResponse ) {
1471
 
1472
- $square_variation_ids = $this->get_attr( 'pull_inventory_variation_ids', [] );
1473
 
1474
  // remove IDs that have already been processed
1475
  $square_variation_ids = array_diff( $square_variation_ids, $processed_ids );
@@ -1480,37 +1521,53 @@ class Manual_Synchronization extends Stepped_Job {
1480
  return;
1481
  }
1482
 
1483
- if ( count( $square_variation_ids ) > 100 ) {
1484
 
1485
- $variation_ids_batch = array_slice( $square_variation_ids, 0, 100 );
1486
 
1487
  $this->set_attr( 'pull_inventory_variation_ids', array_diff( $square_variation_ids, $variation_ids_batch ) );
1488
 
1489
  $square_variation_ids = $variation_ids_batch;
1490
  }
1491
 
1492
- $response = wc_square()->get_api()->batch_retrieve_inventory_counts( [
1493
- 'catalog_object_ids' => array_values( $square_variation_ids ),
1494
- 'location_ids' => [ wc_square()->get_settings_handler()->get_location_id() ],
1495
- ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1496
 
1497
- if ( ! $response->get_data() instanceof BatchRetrieveInventoryCountsResponse ) {
1498
- throw new Framework\SV_WC_Plugin_Exception( 'Response data missing or invalid' );
1499
- }
1500
 
1501
- $response_data = $response->get_data();
 
1502
 
1503
- // if no counts were returned, there's nothing to process
1504
- if ( ! is_array( $response_data->getCounts() ) ) {
 
1505
 
1506
- $this->set_attr( 'processed_square_variation_ids', array_merge( $processed_ids, $square_variation_ids ) );
1507
- return;
1508
- }
 
 
1509
 
1510
- $in_progress['response_data'] = $response_data . '';
1511
  }
1512
 
1513
- foreach ( $response_data->getCounts() as $count ) {
1514
 
1515
  if ( in_array( $count->getCatalogObjectId(), $in_progress['processed_variation_ids'], false ) ) {
1516
  continue;
@@ -1544,7 +1601,7 @@ class Manual_Synchronization extends Stepped_Job {
1544
  $this->set_attr( 'processed_square_variation_ids', array_merge( $processed_ids, $in_progress['processed_variation_ids'] ) );
1545
 
1546
  // clear any in-progress data
1547
- $this->set_attr( 'in_progress_pull_inventory', [] );
1548
  }
1549
 
1550
  /**
@@ -1554,7 +1611,7 @@ class Manual_Synchronization extends Stepped_Job {
1554
  *
1555
  * @param \WC_Product[]|int[] $products products to mark as failed
1556
  */
1557
- protected function mark_failed_products( $products = [] ) {
1558
 
1559
  foreach ( $products as $product ) {
1560
 
@@ -1564,10 +1621,10 @@ class Manual_Synchronization extends Stepped_Job {
1564
  continue;
1565
  }
1566
 
1567
- $record_data = [
1568
  'type' => 'alert',
1569
  'product_id' => $product->get_id(),
1570
- ];
1571
 
1572
  // optionally hide unmatched products from catalog
1573
  if ( wc_square()->get_settings_handler()->is_system_of_record_square() && wc_square()->get_settings_handler()->hide_missing_square_products() ) {
@@ -1579,7 +1636,8 @@ class Manual_Synchronization extends Stepped_Job {
1579
 
1580
  $record_data['product_hidden'] = true;
1581
 
1582
- } catch ( \Exception $e ) {}
 
1583
  }
1584
 
1585
  Records::set_record( $record_data );
@@ -1599,15 +1657,15 @@ class Manual_Synchronization extends Stepped_Job {
1599
 
1600
  if ( ! empty( $product_ids ) ) {
1601
  $category_ids = get_terms(
1602
- [
1603
  'taxonomy' => 'product_cat',
1604
  'fields' => 'ids',
1605
  'object_ids' => $product_ids,
1606
- ]
1607
  );
1608
  }
1609
 
1610
- return ! empty( $category_ids ) && ! is_wp_error( $category_ids ) ? $category_ids : [];
1611
  }
1612
 
1613
 
@@ -1618,21 +1676,21 @@ class Manual_Synchronization extends Stepped_Job {
1618
  */
1619
  protected function assign_next_steps() {
1620
 
1621
- $next_steps = [];
1622
 
1623
  if ( $this->is_system_of_record_woocommerce() ) {
1624
 
1625
  if ( 'delete' === $this->get_attr( 'action' ) ) {
1626
 
1627
- $next_steps = [
1628
  'validate_products',
1629
  'update_matched_products',
1630
  'search_matched_products',
1631
- ];
1632
 
1633
  } else {
1634
 
1635
- $next_steps = [
1636
  'validate_products',
1637
  'extract_category_ids',
1638
  'refresh_category_mappings',
@@ -1641,7 +1699,7 @@ class Manual_Synchronization extends Stepped_Job {
1641
  'update_matched_products',
1642
  'search_matched_products',
1643
  'upsert_new_products',
1644
- ];
1645
 
1646
  // only handle product inventory if enabled
1647
  if ( wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) {
@@ -1649,13 +1707,12 @@ class Manual_Synchronization extends Stepped_Job {
1649
  $next_steps[] = 'pull_inventory';
1650
  }
1651
  }
1652
-
1653
  } elseif ( $this->is_system_of_record_square() ) {
1654
 
1655
- $next_steps = [
1656
  'validate_products',
1657
  'square_sor_sync',
1658
- ];
1659
 
1660
  // only pull product inventory if enabled
1661
  if ( wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) {
33
  use WooCommerce\Square\Handlers\Category;
34
  use WooCommerce\Square\Handlers\Product;
35
 
36
+ defined( 'ABSPATH' ) || exit;
37
 
38
  /**
39
  * Class to represent a single synchronization job triggered manually.
49
  /** @var int the limit for how many inventory changes can be made in a single request */
50
  const BATCH_CHANGE_INVENTORY_LIMIT = 100;
51
 
52
+ /** @var int the limit for how many inventory counts can be requested per batch
53
+ * Square paginates responses in page size of 100.
54
+ * Consider some items can have more than one object returned with different states. */
55
+ const BATCH_INVENTORY_COUNTS_LIMIT = 125;
56
 
57
  /**
58
  * Validates the products attached to this job.
63
 
64
  $product_ids = $this->get_attr( 'product_ids' );
65
 
66
+ $products_query = array(
67
  'include' => $product_ids,
68
  'limit' => -1,
69
+ 'status' => array( 'private', 'publish' ),
70
+ 'return' => 'ids',
71
+ );
 
72
 
73
  if ( 'delete' === $this->get_attr( 'action' ) ) {
74
 
75
+ $products_query['status'] = array( 'trash', 'draft', 'pending', 'private', 'publish' );
 
 
 
76
  }
77
 
78
+ $validated_products = wc_get_products( $products_query );
79
+
80
  $this->set_attr( 'validated_product_ids', $validated_products );
81
 
82
  $this->complete_step( 'validate_products' );
99
  $limits = $catalog_info->get_data()->getLimits();
100
 
101
  $this->set_attr( 'max_objects_to_retrieve', $limits->getBatchRetrieveMaxObjectIds() );
102
+ $this->set_attr( 'max_objects_per_batch', $limits->getBatchUpsertMaxObjectsPerBatch() );
103
+ $this->set_attr( 'max_objects_total', $limits->getBatchUpsertMaxTotalObjects() );
104
  }
105
+ } catch ( Framework\SV_WC_Plugin_Exception $exception ) { // no need to handle errors here
106
+ }
107
 
108
  $this->complete_step( 'update_limits' );
109
  }
135
 
136
  $map = Category::get_map();
137
  $category_ids = $this->get_attr( 'refresh_mappings_category_ids', $this->get_attr( 'category_ids' ) );
138
+ $mapped_categories = array();
139
+ $unmapped_categories = $this->get_attr( 'unmapped_categories', array() );
140
+ $unmapped_category_ids = array();
141
 
142
  if ( empty( $category_ids ) ) {
143
  $this->complete_step( 'refresh_category_mappings' );
154
 
155
  } else {
156
 
157
+ $this->set_attr( 'refresh_mappings_category_ids', array() );
158
  }
159
 
160
  foreach ( $category_ids as $category_id ) {
171
 
172
  if ( ! empty( $mapped_categories ) ) {
173
 
174
+ $square_ids = array_values(
175
+ array_filter(
176
+ array_map(
177
+ function ( $mapped_category ) {
178
+ return isset( $mapped_category['square_id'] ) ? $mapped_category['square_id'] : null;
179
+ },
180
+ $mapped_categories
181
+ )
182
+ )
183
+ );
184
 
185
  $response = wc_square()->get_api()->batch_retrieve_catalog_objects( $square_ids );
186
 
187
  // swap the square ID into the array key for quick lookup
188
+ $mapped_category_audit = array();
189
 
190
  foreach ( $mapped_categories as $mapped_category_id => $mapped_category ) {
191
  $mapped_category_audit[ $mapped_category['square_id'] ] = $mapped_category_id;
227
 
228
  if ( ! empty( $unmapped_category_ids ) ) {
229
 
230
+ $unmapped_category_terms = get_terms(
231
+ array(
232
+ 'taxonomy' => 'product_cat',
233
+ 'include' => $unmapped_category_ids,
234
+ )
235
+ );
236
 
237
  // make the 'name' attribute the array key, for more efficient searching later.
238
  foreach ( $unmapped_category_terms as $unmapped_category_term ) {
257
  */
258
  protected function query_unmapped_categories() {
259
 
260
+ $unmapped_categories = $this->get_attr( 'unmapped_categories', array() );
261
+ $mapped_categories = $this->get_attr( 'mapped_categories', array() );
262
 
263
  if ( empty( $unmapped_categories ) ) {
264
 
266
 
267
  } else {
268
 
269
+ $response = wc_square()->get_api()->search_catalog_objects(
270
+ array(
271
+ 'object_types' => array( 'CATEGORY' ),
272
+ 'cursor' => $this->get_attr( 'unmapped_categories_cursor' ),
273
+ )
274
+ );
275
 
276
  $category_map = Category::get_map();
277
  $categories = $response->get_data()->getObjects();
286
 
287
  $category_id = $unmapped_categories[ $unmapped_category_key ]['term_id'];
288
 
289
+ $category_map[ $category_id ] = array(
290
  'square_id' => $category_object->getId(),
291
  'square_version' => $category_object->getVersion(),
292
+ );
293
 
294
  $mapped_categories[] = $category_id;
295
  unset( $unmapped_categories[ $unmapped_category_key ] );
322
  protected function upsert_categories() {
323
 
324
  $category_ids = $this->get_attr( 'category_ids' );
325
+ $categories = get_terms(
326
+ array(
327
+ 'taxonomy' => 'product_cat',
328
+ 'include' => $category_ids,
329
+ )
330
+ );
331
 
332
+ $batches = array();
333
+ $reverse_map = array();
334
 
335
  // For now, keep it to one category per batch. Since we can still send 1000 batches per request, it's efficient,
336
  // and insulates errors per category rather than a single category error breaking the entire batch it is in.
344
 
345
  $reverse_map[ $square_id ] = $category_id;
346
 
347
+ $catalog_object_data = array(
348
  'type' => 'CATEGORY',
349
  'id' => $square_id,
350
+ 'category_data' => array(
351
  'name' => wp_specialchars_decode( $category->name ), // names are stored encoded in the database
352
+ ),
353
+ );
354
 
355
  if ( 0 < $square_version ) {
356
  $catalog_object_data['version'] = $square_version;
357
  }
358
 
359
+ $batches[] = new \SquareConnect\Model\CatalogObjectBatch( array( 'objects' => array( new \SquareConnect\Model\CatalogObject( $catalog_object_data ) ) ) );
360
  }
361
 
362
  $idempotency_key = wc_square()->get_idempotency_key( md5( serialize( $batches ) . $this->get_attr( 'id' ) ) . '_upsert_categories' );
404
  */
405
  protected function prepare_matched_products_for_upsert() {
406
 
407
+ $product_ids_to_prepare = $this->get_attr( 'product_ids_to_prepare', $this->get_attr( 'validated_product_ids', array() ) );
408
+ $in_progress = $this->get_attr( 'in_progress_prepare_matched_products_for_upsert', array() );
409
 
410
  if ( empty( $product_ids_to_prepare ) ) {
411
  $this->complete_step( 'prepare_matched_products_for_upsert' );
444
 
445
  try {
446
 
447
+ $objects = array();
448
  $api_response = json_decode( $in_progress['api_response'], true );
449
 
450
  if ( isset( $api_response['objects'] ) ) {
453
  }
454
  }
455
 
456
+ $response = new \SquareConnect\Model\BatchRetrieveCatalogObjectsResponse( array( 'objects' => $objects ) );
457
 
458
+ } catch ( \Exception $e ) {
459
+ }
460
  }
461
 
462
  if ( null === $response ) {
476
  return;
477
  }
478
 
479
+ $catalog_objects = isset( $in_progress['catalog_objects'] ) ? $in_progress['catalog_objects'] : array();
480
 
481
  if ( $response && $response_objects = $response->getObjects() ) {
482
 
503
  }
504
  }
505
 
506
+ $matched_products_to_upsert = $this->get_attr( 'matched_products_to_upsert', array() );
507
 
508
  $this->set_attr( 'matched_products_to_upsert', $matched_products_to_upsert + $catalog_objects );
509
  $this->set_attr( 'product_ids_to_prepare', array_diff( $product_ids_to_prepare, $product_ids ) );
510
+ $this->set_attr( 'in_progress_prepare_matched_products_for_upsert', array() );
511
  }
512
 
513
 
520
  */
521
  protected function upsert_matched_products() {
522
 
523
+ $matched_products_to_upsert = $this->get_attr( 'matched_products_to_upsert', array() );
524
 
525
  if ( empty( $matched_products_to_upsert ) ) {
526
 
533
 
534
  if ( isset( $result['processed'] ) ) {
535
 
536
+ $processed_product_ids = $this->get_attr( 'processed_product_ids', array() );
537
 
538
  $this->set_attr( 'processed_product_ids', array_merge( $processed_product_ids, $result['processed'] ) );
539
 
552
  */
553
  protected function update_matched_products() {
554
 
555
+ $product_ids = $this->get_attr( 'matched_product_ids', $this->get_attr( 'validated_product_ids', array() ) );
556
+ $processed_product_ids = $this->get_attr( 'processed_product_ids', array() );
557
 
558
  // remove IDs that have already been processed
559
  $product_ids = array_diff( $product_ids, $processed_product_ids );
574
 
575
  } else {
576
 
577
+ $this->set_attr( 'matched_product_ids', array() );
578
  }
579
 
580
  $products_map = Product::get_square_meta( $product_ids, 'square_item_id' );
590
  throw new Framework\SV_WC_API_Exception( 'Response data is missing' );
591
  }
592
 
593
+ $catalog_objects = array();
594
 
595
  if ( $response->get_data()->getObjects() ) {
596
 
612
  $this->set_attr( 'processed_product_ids', array_merge( $result['processed'], $processed_product_ids ) );
613
 
614
  // any products that were staged but not processed, push to the matched array to try next time
615
+ $matched_product_ids = $this->get_attr( 'matched_product_ids', array() );
616
  $this->set_attr( 'matched_product_ids', array_merge( $result['unprocessed'], $matched_product_ids ) );
617
  }
618
  }
627
  */
628
  protected function search_matched_products() {
629
 
630
+ $product_ids = $this->get_attr( 'search_product_ids', $this->get_attr( 'validated_product_ids', array() ) );
631
+ $processed_product_ids = $this->get_attr( 'processed_product_ids', array() );
632
+ $in_progress = $this->get_attr(
633
+ 'in_progress_search_matched_products',
634
+ array(
635
+ 'unprocessed_search_response' => null,
636
+ 'processed_remote_object_ids' => array(),
637
+ 'catalog_objects_to_update' => array(),
638
+ 'upserting' => false,
639
+ )
640
+ );
641
 
642
  // remove IDs that have already been processed
643
  $product_ids = array_diff( $product_ids, $processed_product_ids );
662
 
663
  } else {
664
 
665
+ $response = wc_square()->get_api()->search_catalog_objects(
666
+ array(
667
+ 'cursor' => $this->get_attr( 'search_products_cursor' ),
668
+ 'object_types' => array( 'ITEM' ),
669
+ 'limit' => $this->get_max_objects_to_retrieve(),
670
+ )
671
+ );
672
 
673
  $search_response = $response->get_data();
674
 
679
  throw new Framework\SV_WC_API_Exception( 'Response data is missing' );
680
  }
681
 
682
+ $catalog_objects = $search_response->getObjects() ?: array();
683
  $cursor = $search_response->getCursor();
684
  $catalog_objects_to_update = $in_progress['catalog_objects_to_update'];
685
 
785
 
786
  if ( ! empty( $result['unprocessed'] ) ) {
787
 
788
+ $catalog_processed = false;
789
+ $remaining_product_ids = array_merge( $result['unprocessed'], $remaining_product_ids );
790
  $in_progress['catalog_objects_to_update'] = array_diff_key( $catalog_objects_to_update, array_flip( $processed_product_ids ) );
791
 
792
  } else {
800
  if ( ! $catalog_processed && ! empty( $remaining_product_ids ) ) {
801
 
802
  $this->set_attr( 'search_products_cursor', $cursor );
803
+ $this->set_attr( 'search_product_ids', $remaining_product_ids );
804
 
805
  } else {
806
 
815
  */
816
  protected function upsert_new_products() {
817
 
818
+ $product_ids = $this->get_attr( 'upsert_new_product_ids', $this->get_attr( 'validated_product_ids', array() ) );
819
+ $processed_product_ids = $this->get_attr( 'processed_product_ids', array() );
820
 
821
  // remove IDs that have already been processed
822
  $product_ids = array_diff( $product_ids, $processed_product_ids );
827
  return;
828
  }
829
 
830
+ $catalog_objects = array();
831
 
832
  foreach ( $product_ids as $product_id ) {
833
 
834
+ $catalog_objects[ $product_id ] = new CatalogObject(
835
+ array(
836
+ 'type' => 'ITEM',
837
+ 'item_data' => new \SquareConnect\Model\CatalogItem(),
838
+ )
839
+ );
840
  }
841
 
842
  $result = $this->upsert_catalog_objects( $catalog_objects );
879
 
880
  $is_delete_action = 'delete' === $this->get_attr( 'action' );
881
  $product_ids = array_keys( $objects );
882
+ $original_square_image_ids = array();
883
+ $staged_product_ids = array();
884
+ $successful_product_ids = array();
885
  $total_object_count = 0;
886
+ $batches = array();
887
+ $result = array(
888
+ 'processed' => array(),
889
  'unprocessed' => $product_ids,
890
+ );
891
+
892
+ $in_progress = $this->get_attr(
893
+ 'in_progress_upsert_catalog_objects',
894
+ array(
895
+ 'batches' => array(),
896
+ 'staged_product_ids' => array(),
897
+ 'total_object_count' => null,
898
+ 'unprocessed_upsert_response' => null,
899
+ 'mapped_client_item_ids' => array(),
900
+ 'processed_remote_catalog_item_ids' => array(),
901
+ )
902
+ );
903
 
904
  if ( null === $in_progress['unprocessed_upsert_response'] ) {
905
 
908
 
909
  $staged_product_ids = $in_progress['staged_product_ids'];
910
  $total_object_count = $in_progress['total_object_count'];
911
+ $batches = array_map(
912
+ static function ( $batch_data ) {
913
+ return ObjectSerializer::deserialize( json_decode( $batch_data, false ), CatalogObjectBatch::class );
914
+ },
915
+ $in_progress['batches']
916
+ );
917
  }
918
 
919
  foreach ( $objects as $product_id => $object ) {
938
  $object = $this->convert_to_catalog_object( $object );
939
  }
940
 
941
+ $product = wc_get_product( $product_id );
942
  $original_square_image_ids[ $product_id ] = $product->get_meta( '_square_item_image_id' );
943
 
944
  $catalog_item = new Catalog_Item( $product, $is_delete_action );
947
 
948
  if ( $this->get_max_objects_total() >= $object_count + $total_object_count ) {
949
  $batches[] = $batch;
950
+ $total_object_count += $object_count;
951
  $staged_product_ids[] = $product_id;
952
  } else {
953
  break;
1031
  wc_square()->log( 'Mapped ' . count( $in_progress['mapped_client_item_ids'] ) . ' Square IDs in ' . $duration . 's' );
1032
  }
1033
 
1034
+ $pull_inventory_variation_ids = $this->get_attr( 'pull_inventory_variation_ids', array() );
1035
 
1036
  wc_square()->log( 'Storing Square item data to WooCommerce products' );
1037
 
1061
  continue;
1062
  }
1063
 
1064
+ Product::update_square_meta(
1065
+ $product,
1066
+ array(
1067
+ 'item_id' => $remote_item_id,
1068
+ 'item_version' => $remote_catalog_item->getVersion(),
1069
+ 'item_image_id' => $remote_catalog_item->getImageId(),
1070
+ )
1071
+ );
1072
 
1073
  $successful_product_ids[] = $product->get_id();
1074
 
1080
 
1081
  $pull_inventory_variation_ids[] = $catalog_item_variation->getId();
1082
 
1083
+ Product::update_square_meta(
1084
+ $product_variation,
1085
+ array(
1086
+ 'item_variation_id' => $catalog_item_variation->getId(),
1087
+ 'item_variation_version' => $catalog_item_variation->getVersion(),
1088
+ )
1089
+ );
1090
  }
1091
  }
1092
  }
1104
 
1105
  // If there is a local image which is different from the last uploaded image
1106
  // Or if the remote square image id has changed
1107
+ if ( ( $local_image_id && $local_image_id !== $product->get_meta( '_square_uploaded_image_id' ) ) ||
1108
  ( ! ( $original_square_image_ids[ $product_id ] && $original_square_image_ids[ $product_id ] === $product->get_meta( '_square_item_image_id' ) ) ) ) {
1109
  // there is no batch image endpoint
1110
  $this->push_product_image( $product );
1126
  // log any failed products
1127
  foreach ( array_diff( $staged_product_ids, $successful_product_ids ) as $product_id ) {
1128
 
1129
+ Records::set_record(
1130
+ array(
1131
+ 'type' => 'alert',
1132
+ 'product_id' => $product_id,
1133
+ 'message' => sprintf(
1134
+ /* translators: Placeholder: %s - product ID */
1135
+ esc_html__( 'Product %s could not be updated in Square.', 'woocommerce-square' ),
1136
+ '<a href="' . esc_url( get_edit_post_link( $product_id ) ) . '">' . $product_id . '</a>'
1137
+ ),
1138
+ )
1139
+ );
1140
  }
1141
 
1142
  $this->set_attr( 'in_progress_upsert_catalog_objects', null );
1216
  */
1217
  protected function push_inventory() {
1218
 
1219
+ $product_ids = $this->get_attr( 'inventory_push_product_ids', array() );
1220
+ $inventory_changes = array();
1221
  $inventory_change_count = 0;
1222
 
1223
  foreach ( $product_ids as $key => $product_id ) {
1226
 
1227
  if ( $product instanceof \WC_Product ) {
1228
 
1229
+ $product_inventory_changes = array();
1230
 
1231
  if ( $product->is_type( 'variable' ) && $product->has_child() ) {
1232
 
1239
  $product_inventory_changes[] = $inventory_change;
1240
  }
1241
  }
 
1242
  } elseif ( $square_variation_id = Product::get_square_item_variation_id( $product_id, false ) ) {
1243
 
1244
  if ( $inventory_change = Product::get_inventory_change_physical_count_type( $product ) ) {
1257
 
1258
  break;
1259
  }
 
1260
  } else {
1261
 
1262
  unset( $product_ids[ $key ] );
1286
  */
1287
  protected function square_sor_sync() {
1288
 
1289
+ $synced_product_ids = $this->get_attr( 'validated_product_ids', array() );
1290
+ $processed_product_ids = $this->get_attr( 'processed_product_ids', array() );
1291
  $unprocessed_product_ids = array_diff( $synced_product_ids, $processed_product_ids );
1292
  $catalog_processed = $this->get_attr( 'catalog_processed', false );
1293
 
1313
 
1314
  $cursor = $this->get_attr( 'square_sor_cursor' );
1315
 
1316
+ $response = wc_square()->get_api()->search_catalog_objects(
1317
+ array(
1318
+ 'cursor' => $cursor,
1319
+ 'object_types' => array( 'ITEM' ),
1320
+ 'include_related_objects' => true,
1321
+ 'limit' => $this->get_max_objects_to_retrieve(),
1322
+ )
1323
+ );
1324
 
1325
  $response_data = $response->get_data();
1326
 
1341
  $catalog_processed = ! $cursor;
1342
  $this->set_attr( 'catalog_processed', $catalog_processed );
1343
 
1344
+ } catch ( \Exception $exception ) { // bail early and fail for any API and plugin errors
 
1345
 
1346
  $this->fail( 'Product sync failed. ' . $exception->getMessage() );
1347
  return;
1348
  }
1349
 
1350
+ $related_objects = $response_data->getRelatedObjects();
1351
 
1352
  if ( $related_objects && is_array( $related_objects ) ) {
1353
  // first import any related categories
1358
  }
1359
  }
1360
 
1361
+ $pull_inventory_variation_ids = $this->get_attr( 'pull_inventory_variation_ids', array() );
1362
 
1363
  /** @var \SquareConnect\Model\CatalogObject[] */
1364
+ $catalog_objects = $products_to_update = array();
1365
 
1366
  wc_square()->log( 'Searching for products in ' . count( $response_data->getObjects() ) . ' Square objects' );
1367
 
1462
  if ( ! $product->get_image_id() && $square_object->getImageId() ) {
1463
  Product::update_image_from_square( $product, $square_object->getImageId() );
1464
  }
 
1465
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
1466
 
1467
+ $this->mark_failed_products( array( $product ) );
1468
  }
1469
 
1470
  $processed_product_ids[] = $product->get_id();
1487
  */
1488
  protected function pull_inventory() {
1489
 
1490
+ $processed_ids = $this->get_attr( 'processed_square_variation_ids', array() );
1491
 
1492
+ $in_progress = wp_parse_args(
1493
+ $this->get_attr(
1494
+ 'in_progress_pull_inventory',
1495
+ array()
1496
+ ),
1497
+ array(
1498
+ 'response_data' => null,
1499
+ 'processed_variation_ids' => array(),
1500
+ )
1501
+ );
1502
 
1503
  $response_data = null;
1504
 
1510
  // if the saved response was somehow corrupted, start over
1511
  if ( ! $response_data instanceof BatchRetrieveInventoryCountsResponse ) {
1512
 
1513
+ $square_variation_ids = $this->get_attr( 'pull_inventory_variation_ids', array() );
1514
 
1515
  // remove IDs that have already been processed
1516
  $square_variation_ids = array_diff( $square_variation_ids, $processed_ids );
1521
  return;
1522
  }
1523
 
1524
+ if ( count( $square_variation_ids ) > self::BATCH_INVENTORY_COUNTS_LIMIT ) {
1525
 
1526
+ $variation_ids_batch = array_slice( $square_variation_ids, 0, self::BATCH_INVENTORY_COUNTS_LIMIT );
1527
 
1528
  $this->set_attr( 'pull_inventory_variation_ids', array_diff( $square_variation_ids, $variation_ids_batch ) );
1529
 
1530
  $square_variation_ids = $variation_ids_batch;
1531
  }
1532
 
1533
+ $cursor = '';
1534
+ $response_counts = array();
1535
+ $location_ids = array( wc_square()->get_settings_handler()->get_location_id() );
1536
+ $catalog_object_ids = array_values( $square_variation_ids );
1537
+
1538
+ // Repeat fetching objects using the cursor when the results are paginated.
1539
+ do {
1540
+ $response = wc_square()->get_api()->batch_retrieve_inventory_counts(
1541
+ array(
1542
+ 'catalog_object_ids' => $catalog_object_ids,
1543
+ 'location_ids' => $location_ids,
1544
+ 'cursor' => $cursor,
1545
+ )
1546
+ );
1547
+
1548
+ if ( ! $response->get_data() instanceof BatchRetrieveInventoryCountsResponse ) {
1549
+ throw new Framework\SV_WC_Plugin_Exception( 'Response data missing or invalid' );
1550
+ }
1551
 
1552
+ $response_data = $response->get_data();
 
 
1553
 
1554
+ // if no counts were returned, there's nothing to process
1555
+ if ( ! is_array( $response_data->getCounts() ) ) {
1556
 
1557
+ $this->set_attr( 'processed_square_variation_ids', array_merge( $processed_ids, $square_variation_ids ) );
1558
+ return;
1559
+ }
1560
 
1561
+ $in_progress['response_data'] = $response_data . '';
1562
+
1563
+ // Store the response counts to be processed later.
1564
+ $response_counts = array_merge( $response_counts, $response_data->getCounts() );
1565
+ $cursor = $response->get_data()->getCursor();
1566
 
1567
+ } while ( ! empty( $cursor ) );
1568
  }
1569
 
1570
+ foreach ( $response_counts as $count ) {
1571
 
1572
  if ( in_array( $count->getCatalogObjectId(), $in_progress['processed_variation_ids'], false ) ) {
1573
  continue;
1601
  $this->set_attr( 'processed_square_variation_ids', array_merge( $processed_ids, $in_progress['processed_variation_ids'] ) );
1602
 
1603
  // clear any in-progress data
1604
+ $this->set_attr( 'in_progress_pull_inventory', array() );
1605
  }
1606
 
1607
  /**
1611
  *
1612
  * @param \WC_Product[]|int[] $products products to mark as failed
1613
  */
1614
+ protected function mark_failed_products( $products = array() ) {
1615
 
1616
  foreach ( $products as $product ) {
1617
 
1621
  continue;
1622
  }
1623
 
1624
+ $record_data = array(
1625
  'type' => 'alert',
1626
  'product_id' => $product->get_id(),
1627
+ );
1628
 
1629
  // optionally hide unmatched products from catalog
1630
  if ( wc_square()->get_settings_handler()->is_system_of_record_square() && wc_square()->get_settings_handler()->hide_missing_square_products() ) {
1636
 
1637
  $record_data['product_hidden'] = true;
1638
 
1639
+ } catch ( \Exception $e ) {
1640
+ }
1641
  }
1642
 
1643
  Records::set_record( $record_data );
1657
 
1658
  if ( ! empty( $product_ids ) ) {
1659
  $category_ids = get_terms(
1660
+ array(
1661
  'taxonomy' => 'product_cat',
1662
  'fields' => 'ids',
1663
  'object_ids' => $product_ids,
1664
+ )
1665
  );
1666
  }
1667
 
1668
+ return ! empty( $category_ids ) && ! is_wp_error( $category_ids ) ? $category_ids : array();
1669
  }
1670
 
1671
 
1676
  */
1677
  protected function assign_next_steps() {
1678
 
1679
+ $next_steps = array();
1680
 
1681
  if ( $this->is_system_of_record_woocommerce() ) {
1682
 
1683
  if ( 'delete' === $this->get_attr( 'action' ) ) {
1684
 
1685
+ $next_steps = array(
1686
  'validate_products',
1687
  'update_matched_products',
1688
  'search_matched_products',
1689
+ );
1690
 
1691
  } else {
1692
 
1693
+ $next_steps = array(
1694
  'validate_products',
1695
  'extract_category_ids',
1696
  'refresh_category_mappings',
1699
  'update_matched_products',
1700
  'search_matched_products',
1701
  'upsert_new_products',
1702
+ );
1703
 
1704
  // only handle product inventory if enabled
1705
  if ( wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) {
1707
  $next_steps[] = 'pull_inventory';
1708
  }
1709
  }
 
1710
  } elseif ( $this->is_system_of_record_square() ) {
1711
 
1712
+ $next_steps = array(
1713
  'validate_products',
1714
  'square_sor_sync',
1715
+ );
1716
 
1717
  // only pull product inventory if enabled
1718
  if ( wc_square()->get_settings_handler()->is_inventory_sync_enabled() ) {
includes/Sync/Product_Import.php CHANGED
@@ -30,7 +30,7 @@ use WooCommerce\Square\Handlers\Product;
30
  use WooCommerce\Square\Sync\Records\Record;
31
  use WooCommerce\Square\Utilities\Money_Utility;
32
 
33
- defined( 'ABSPATH' ) or exit;
34
 
35
  /**
36
  * Class to represent a synchronization job to import products from Square.
@@ -42,10 +42,13 @@ class Product_Import extends Stepped_Job {
42
 
43
  protected function assign_next_steps() {
44
 
45
- $this->set_attr( 'next_steps', [
46
- 'import_products',
47
- 'import_inventory',
48
- ] );
 
 
 
49
  }
50
 
51
 
@@ -83,27 +86,28 @@ class Product_Import extends Stepped_Job {
83
  */
84
  protected function import_products() {
85
 
86
- $products_imported = $this->get_attr( 'processed_product_ids', [] );
87
- $cursor = $this->get_attr( 'fetch_products_cursor' );
88
- $skipped_products = $this->get_attr( 'skipped_products', [] );
 
89
 
90
- $response = wc_square()->get_api()->search_catalog_objects( [
91
- 'cursor' => $cursor,
92
- 'object_types' => [ 'ITEM' ],
93
- 'include_related_objects' => true,
94
- 'limit' => $this->get_import_api_limit(),
95
- ] );
 
 
96
 
97
  $related_objects = $response->get_data()->getRelatedObjects();
 
98
 
99
  if ( $related_objects && is_array( $related_objects ) ) {
100
-
101
- // first import any related categories
102
  foreach ( $related_objects as $related_object ) {
103
 
104
  if ( 'CATEGORY' === $related_object->getType() ) {
105
-
106
- Category::import_or_update( $related_object );
107
  }
108
  }
109
  }
@@ -119,44 +123,87 @@ class Product_Import extends Stepped_Job {
119
  break;
120
  }
121
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  $item_id = $catalog_object->getId();
123
 
124
- // ignore items not available at our location
125
- if ( ! $catalog_object->getPresentAtAllLocations() && ( ! is_array( $catalog_object->getPresentAtLocationIds() ) || ! in_array( wc_square()->get_settings_handler()->get_location_id(), $catalog_object->getPresentAtLocationIds(), true ) ) ) {
126
 
127
  $skipped_products[ $item_id ] = null;
128
  continue;
129
  }
130
 
131
- // if we have a match for this item ID, skip it - it will already be synced
132
- if ( $product_id = Product::get_product_id_by_square_id( $item_id ) ) {
133
 
134
- $skipped_products[ $item_id ] = null;
135
- continue;
 
136
  }
137
 
138
- // look in variation SKUs for a match - if so, skip the parent item, a normal sync should link it automatically
139
- if ( $this->item_variation_has_matching_sku( $catalog_object ) ) {
 
 
 
140
  continue;
141
  }
142
 
143
- $product_id = $this->import_product( $catalog_object );
 
 
 
 
 
 
 
 
 
 
 
144
 
145
- if ( $product_id ) {
 
146
 
147
- Product::set_synced_with_square( $product_id );
 
 
148
 
149
- $products_imported[] = $product_id;
 
 
 
 
 
 
 
 
 
 
 
 
150
  }
151
  }
152
 
153
  if ( $time_exceeded ) {
154
 
155
- wc_square()->log( '[Time Exceeded] Imported Products Count: ' . count( $products_imported ) );
156
 
157
  } else {
158
 
159
- wc_square()->log( 'Imported Products Count: ' . count( $products_imported ) );
 
160
 
161
  $cursor = $response->get_data()->getCursor();
162
  $this->set_attr( 'fetch_products_cursor', $cursor );
@@ -168,7 +215,8 @@ class Product_Import extends Stepped_Job {
168
  }
169
 
170
  $this->set_attr( 'skipped_products', $skipped_products );
171
- $this->set_attr( 'processed_product_ids', $products_imported );
 
172
  }
173
 
174
 
@@ -181,23 +229,28 @@ class Product_Import extends Stepped_Job {
181
  */
182
  protected function import_inventory() {
183
 
184
- $search_result = wc_square()->get_api()->search_catalog_objects( [
185
- 'object_types' => [ 'ITEM_VARIATION' ],
186
- 'limit' => 100,
187
- 'cursor' => $this->get_attr( 'import_inventory_cursor', null ),
188
- ] );
 
 
189
 
190
  $cursor = $search_result->get_data()->getCursor();
191
- $variation_ids = array_map( static function( \SquareConnect\Model\CatalogObject $catalog_object ) {
192
-
193
- return $catalog_object->getId();
194
-
195
- }, $search_result->get_data()->getObjects() );
196
-
197
- $result = wc_square()->get_api()->batch_retrieve_inventory_counts( [
198
- 'catalog_object_ids' => $variation_ids,
199
- 'location_ids' => [ wc_square()->get_settings_handler()->get_location_id() ],
200
- ] );
 
 
 
201
 
202
  foreach ( $result->get_counts() as $inventory_count ) {
203
 
@@ -223,6 +276,30 @@ class Product_Import extends Stepped_Job {
223
  }
224
 
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  /**
227
  * Determines whether a SKU within a catalog item is found in WooCommerce.
228
  *
@@ -246,88 +323,16 @@ class Product_Import extends Stepped_Job {
246
 
247
 
248
  /**
249
- * Creates a product from a catalog object.
250
  *
251
  * @since 2.0.0
252
  *
253
- * @param \SquareConnect\Model\CatalogObject $catalog_object the catalog object
254
  * @return int|null
255
  */
256
- private function import_product( $catalog_object ) {
257
-
258
- $product_id = 0;
259
-
260
  try {
261
-
262
- // validate permissions
263
- if ( ! current_user_can( 'publish_products' ) ) {
264
- throw new Framework\SV_WC_Plugin_Exception( __( 'You do not have permission to create products', 'woocommerce-square' ) );
265
- }
266
-
267
- // sanity check for valid API data
268
- if ( ! $catalog_object instanceof \SquareConnect\Model\CatalogObject || ! $catalog_object->getItemData() instanceof \SquareConnect\Model\CatalogItem ) {
269
- throw new Framework\SV_WC_Plugin_Exception( __( 'Invalid data', 'woocommerce-square' ) );
270
- }
271
-
272
- $data = $this->extract_product_data( $catalog_object );
273
-
274
- /**
275
- * Filters the data that is used to create a new WooCommerce product during import.
276
- *
277
- * @since 2.0.0
278
- *
279
- * @param array $data product data
280
- * @param \SquareConnect\Model\CatalogObject $catalog_object the catalog object from the Square API
281
- * @param Product_Import $this import class instance
282
- */
283
- $data = apply_filters( 'woocommerce_square_create_product_data', $data, $catalog_object, $this );
284
-
285
- // validate title field
286
- if ( ! isset( $data['title'] ) ) {
287
- throw new Framework\SV_WC_Plugin_Exception( sprintf( __( 'Missing parameter %s', 'woocommerce-square' ), 'title' ) );
288
- }
289
-
290
- // set default type
291
- if ( ! isset( $data['type'] ) ) {
292
- $data['type'] = 'simple';
293
- }
294
-
295
- // set default catalog_visibility
296
- if ( ! isset( $data['catalog_visibility'] ) ) {
297
- $data['catalog_visibility'] = 'visible';
298
- }
299
-
300
- // validate type
301
- if ( ! array_key_exists( wc_clean( $data['type'] ), wc_get_product_types() ) ) {
302
- throw new Framework\SV_WC_Plugin_Exception( sprintf( __( 'Invalid product type - the product type must be any of these: %s', 'woocommerce-square' ), implode( ', ', array_keys( wc_get_product_types() ) ) ) );
303
- }
304
-
305
- // set description
306
- $post_content = isset( $data['description'] ) ? wc_clean( $data['description'] ) : '';
307
- if ( $post_content && isset( $data['enable_html_description'] ) && true === $data['enable_html_description'] ) {
308
-
309
- $post_content = $data['description'];
310
- }
311
-
312
- $new_product = [
313
- 'post_title' => wc_clean( $data['title'] ),
314
- 'post_status' => isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish',
315
- 'post_type' => 'product',
316
- 'post_content' => isset( $data['description'] ) ? $post_content : '',
317
- 'post_author' => get_current_user_id(),
318
- 'menu_order' => isset( $data['menu_order'] ) ? (int) $data['menu_order'] : 0,
319
- ];
320
-
321
- if ( ! empty( $data['name'] ) ) {
322
- $new_product = array_merge( $new_product, [ 'post_name' => sanitize_title( $data['name'] ) ] );
323
- }
324
-
325
- // attempt to create the new product
326
- $product_id = wp_insert_post( $new_product, true );
327
-
328
- if ( is_wp_error( $product_id ) ) {
329
- throw new Framework\SV_WC_Plugin_Exception( $product_id->get_error_message() );
330
- }
331
 
332
  // save product meta fields
333
  $this->save_product_meta( $product_id, $data );
@@ -355,44 +360,106 @@ class Product_Import extends Stepped_Job {
355
 
356
  // clear cache/transients
357
  wc_delete_product_transients( $product_id );
358
-
359
  } catch ( Framework\SV_WC_Plugin_Exception $e ) {
 
 
 
360
 
361
- if ( $catalog_object instanceof \SquareConnect\Model\CatalogObject && $catalog_object->getItemData() instanceof \SquareConnect\Model\CatalogItem ) {
 
362
 
363
- $message = sprintf(
364
- /* translators: Placeholders: %1$s - Square item name, %2$s - failure reason */
365
- __( 'Could not import "%1$s" from Square. %2$s', 'woocommerce-square' ),
366
- $catalog_object->getItemData()->getName(),
367
- $e->getMessage()
368
- );
369
 
370
- // use a generic alert for invalid data
371
- } else {
 
 
 
 
 
 
 
 
 
 
372
 
373
- $message = sprintf(
374
- /* translators: Placeholders: %s - failure reason */
375
- __( 'Could not import item from Square. %s', 'woocommerce-square' ),
376
- $e->getMessage()
377
- );
 
378
  }
379
 
380
- // alert for failed product imports
381
- Records::set_record( [
382
- 'type' => 'alert',
383
- 'message' => $message,
384
- ] );
385
 
386
- wc_square()->log( 'Error creating product during import: ' . $e->getMessage() );
 
 
 
387
 
388
- // remove the product when creation fails
389
- $this->clear_product( $product_id );
 
 
 
 
 
 
 
 
 
390
  $product_id = 0;
 
 
391
  }
392
 
393
  return $product_id;
394
  }
395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
 
397
  /**
398
  * Extracts product data from a CatalogObject to an array of data.
@@ -400,10 +467,11 @@ class Product_Import extends Stepped_Job {
400
  * @since 2.0.0
401
  *
402
  * @param \SquareConnect\Model\CatalogObject $catalog_object the catalog object
 
403
  * @return array|null
404
  * @throws Framework\SV_WC_Plugin_Exception
405
  */
406
- protected function extract_product_data( $catalog_object ) {
407
 
408
  $variations = $catalog_object->getItemData()->getVariations();
409
 
@@ -414,23 +482,22 @@ class Product_Import extends Stepped_Job {
414
 
415
  $category_id = Category::get_category_id_by_square_id( $catalog_object->getItemData()->getCategoryId() );
416
 
417
- $data = [
418
  'title' => $catalog_object->getItemData()->getName(),
419
- 'type' => 1 === count( $variations ) ? 'simple' : 'variable',
 
420
  'description' => $catalog_object->getItemData()->getDescription(),
421
  'image_id' => $catalog_object->getImageId(),
422
- 'categories' => [ $category_id ],
423
- 'square_meta' => [
424
  'item_id' => $catalog_object->getId(),
425
  'item_version' => $catalog_object->getVersion(),
426
- ],
427
- ];
428
 
429
  // variable product
430
- if ( 1 < count( $variations ) ) {
431
-
432
- $data['type'] = 'variable';
433
- $data['variations'] = [];
434
 
435
  foreach ( $variations as $variation ) {
436
 
@@ -446,41 +513,42 @@ class Product_Import extends Stepped_Job {
446
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
447
 
448
  // alert for failed variation imports
449
- Records::set_record( [
450
- 'type' => 'alert',
451
- 'message' => sprintf(
452
- /* translators: Placeholders: %1$s - Square item name, %2$s - Square item variation name, %3$s - failure reason */
453
- __( 'Could not import "%1$s - %2$s" from Square. %3$s', 'woocommerce-square' ),
454
- $catalog_object->getItemData()->getName(),
455
- $variation->getItemVariationData()->getName(),
456
- $exception->getMessage()
457
- ),
458
- ] );
 
 
459
  }
460
  }
461
 
462
- $data['attributes'] = [ [
463
- 'name' => 'Attribute',
464
- 'slug' => 'attribute',
465
- 'position' => 0,
466
- 'visible' => true,
467
- 'variation' => true,
468
- 'options' => wp_list_pluck( $data['variations'], 'name' ),
469
- ] ];
470
-
471
- // simple product
472
- } else {
473
-
474
  $variation = $this->extract_square_item_variation_data( $variations[0] );
475
 
476
  $data['type'] = 'simple';
477
- $data['sku'] = $variation['sku'] ?: null;
478
- $data['regular_price'] = $variation['regular_price'] ?: null;
479
- $data['stock_quantity'] = $variation['stock_quantity'] ?: null;
480
- $data['managing_stock'] = $variation['managing_stock'] ?: null;
481
 
482
- $data['square_meta']['item_variation_id'] = $variation['square_meta']['item_variation_id'] ?: null;
483
- $data['square_meta']['item_variation_version'] = $variation['square_meta']['item_variation_version'] ?: null;
484
  }
485
 
486
  return $data;
@@ -504,24 +572,24 @@ class Product_Import extends Stepped_Job {
504
  throw new Framework\SV_WC_Plugin_Exception( __( 'Items with variable pricing cannot be imported.', 'woocommerce-square' ) );
505
  }
506
 
507
- $data = [
508
- 'name' => $variation_data->getName() ?: '',
509
- 'sku' => $variation_data->getSku() ?: '',
510
  'regular_price' => $variation_data->getPriceMoney() && $variation_data->getPriceMoney()->getAmount() ? Money_Utility::cents_to_float( $variation->getItemVariationData()->getPriceMoney()->getAmount() ) : null,
511
  'stock_quantity' => null,
512
  'managing_stock' => true,
513
- 'square_meta' => [
514
  'item_variation_id' => $variation->getId(),
515
  'item_variation_version' => $variation->getVersion(),
516
- ],
517
- 'attributes' => [
518
- [
519
  'name' => 'Attribute',
520
  'is_variation' => true,
521
- 'option' => $variation_data->getName() ?: '',
522
- ],
523
- ],
524
- ];
525
 
526
  return $data;
527
  }
@@ -556,9 +624,7 @@ class Product_Import extends Stepped_Job {
556
  $product_type = wc_clean( $data['type'] );
557
 
558
  wp_set_object_terms( $product_id, $product_type, 'product_type' );
559
-
560
  } else {
561
-
562
  $_product_type = get_the_terms( $product_id, 'product_type' );
563
 
564
  if ( is_array( $_product_type ) ) {
@@ -572,10 +638,7 @@ class Product_Import extends Stepped_Job {
572
  add_post_meta( $product_id, 'total_sales', '0', true );
573
 
574
  // catalog visibility
575
- if ( isset( $data['catalog_visibility'] ) ) {
576
-
577
- update_post_meta( $product_id, '_visibility', wc_clean( $data['catalog_visibility'] ) );
578
- }
579
 
580
  // sku
581
  if ( isset( $data['sku'] ) ) {
@@ -601,9 +664,7 @@ class Product_Import extends Stepped_Job {
601
 
602
  throw new Framework\SV_WC_Plugin_Exception( __( 'The SKU already exists on another product', 'woocommerce-square' ) );
603
  }
604
-
605
  } else {
606
-
607
  update_post_meta( $product_id, '_sku', '' );
608
  }
609
  }
@@ -612,7 +673,7 @@ class Product_Import extends Stepped_Job {
612
  // attributes
613
  if ( isset( $data['attributes'] ) ) {
614
 
615
- $attributes = [];
616
 
617
  foreach ( $data['attributes'] as $attribute ) {
618
 
@@ -653,7 +714,7 @@ class Product_Import extends Stepped_Job {
653
 
654
  } else {
655
 
656
- $values = [];
657
  }
658
 
659
  // update post terms
@@ -665,18 +726,16 @@ class Product_Import extends Stepped_Job {
665
  if ( ! empty( $values ) ) {
666
 
667
  // add attribute to array, but don't set values
668
- $attributes[ $taxonomy ] = [
669
  'name' => $taxonomy,
670
  'value' => '',
671
  'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0',
672
  'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0,
673
  'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0,
674
  'is_taxonomy' => $is_taxonomy,
675
- ];
676
  }
677
-
678
  } elseif ( isset( $attribute['options'] ) ) {
679
-
680
  // array based
681
  if ( is_array( $attribute['options'] ) ) {
682
 
@@ -688,14 +747,14 @@ class Product_Import extends Stepped_Job {
688
  }
689
 
690
  // custom attribute - add attribute to array and set the values
691
- $attributes[ $attribute_slug ] = [
692
  'name' => wc_clean( $attribute['name'] ),
693
  'value' => $values,
694
  'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0',
695
  'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0,
696
  'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0,
697
  'is_taxonomy' => $is_taxonomy,
698
- ];
699
  }
700
  }
701
 
@@ -705,7 +764,7 @@ class Product_Import extends Stepped_Job {
705
  }
706
 
707
  // sales and prices
708
- if ( in_array( $product_type, [ 'variable', 'grouped' ] ) ) {
709
 
710
  // variable and grouped products have no prices
711
  update_post_meta( $product_id, '_regular_price', '' );
@@ -774,8 +833,10 @@ class Product_Import extends Stepped_Job {
774
  protected function save_variations( $product_id, $data ) {
775
  global $wpdb;
776
 
777
- $variations = $data['variations'];
778
- $attributes = (array) maybe_unserialize( get_post_meta( $product_id, '_product_attributes', true ) );
 
 
779
 
780
  foreach ( $variations as $menu_order => $variation ) {
781
 
@@ -787,13 +848,14 @@ class Product_Import extends Stepped_Job {
787
  $variation_id = wc_get_product_id_by_sku( $variation_sku );
788
  }
789
 
 
790
  $variation_post_title = sprintf( __( 'Variation #%1$s of %2$s', 'woocommerce-square' ), $variation_id, esc_html( get_the_title( $product_id ) ) );
791
 
792
  // update or add post
793
  if ( ! $variation_id ) {
794
 
795
  $post_status = ( isset( $variation['visible'] ) && false === $variation['visible'] ) ? 'private' : 'publish';
796
- $new_variation = [
797
  'post_title' => $variation_post_title,
798
  'post_content' => '',
799
  'post_status' => $post_status,
@@ -801,7 +863,7 @@ class Product_Import extends Stepped_Job {
801
  'post_parent' => $product_id,
802
  'post_type' => 'product_variation',
803
  'menu_order' => $menu_order,
804
- ];
805
 
806
  $variation_id = wp_insert_post( $new_variation );
807
 
@@ -816,7 +878,10 @@ class Product_Import extends Stepped_Job {
816
 
817
  } else {
818
 
819
- $update_variation = [ 'post_title' => $variation_post_title, 'menu_order' => $menu_order ];
 
 
 
820
 
821
  if ( isset( $variation['visible'] ) ) {
822
 
@@ -825,7 +890,12 @@ class Product_Import extends Stepped_Job {
825
  $update_variation['post_status'] = $post_status;
826
  }
827
 
828
- $wpdb->update( $wpdb->posts, $update_variation, [ 'ID' => $variation_id ] );
 
 
 
 
 
829
 
830
  /**
831
  * Fired after updating a product variation during an import from Square.
@@ -865,7 +935,6 @@ class Product_Import extends Stepped_Job {
865
 
866
  throw new Framework\SV_WC_Plugin_Exception( __( 'The SKU already exists on another product', 'woocommerce-square' ) );
867
  }
868
-
869
  } else {
870
 
871
  update_post_meta( $variation_id, '_sku', '' );
@@ -874,7 +943,7 @@ class Product_Import extends Stepped_Job {
874
  }
875
 
876
  update_post_meta( $variation_id, '_manage_stock', 'yes' );
877
- update_post_meta( $variation_id, '_backorders', 'no');
878
 
879
  $this->wc_save_product_price( $variation_id, $variation['regular_price'] );
880
 
@@ -890,7 +959,7 @@ class Product_Import extends Stepped_Job {
890
  // update taxonomies
891
  if ( isset( $variation['attributes'] ) ) {
892
 
893
- $updated_attribute_keys = [];
894
 
895
  foreach ( $variation['attributes'] as $attribute_key => $attribute ) {
896
 
@@ -899,7 +968,7 @@ class Product_Import extends Stepped_Job {
899
  }
900
 
901
  $taxonomy = 0;
902
- $_attribute = [];
903
 
904
  if ( isset( $attribute['slug'] ) ) {
905
 
@@ -936,7 +1005,7 @@ class Product_Import extends Stepped_Job {
936
  }
937
 
938
  // remove old taxonomies attributes so data is kept up to date - first get attribute key names
939
- $delete_attribute_keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->postmeta} WHERE meta_key LIKE 'attribute_%%' AND meta_key NOT IN ( '" . implode( "','", $updated_attribute_keys ) . "' ) AND post_id = %d;", $variation_id ) );
940
 
941
  foreach ( $delete_attribute_keys as $key ) {
942
 
@@ -966,6 +1035,11 @@ class Product_Import extends Stepped_Job {
966
  do_action( 'woocommerce_square_save_product_variation', $variation_id, $menu_order, $variation );
967
  }
968
 
 
 
 
 
 
969
  // update parent if variable so price sorting works and stays in sync with the cheapest child
970
  \WC_Product_Variable::sync( $product_id );
971
 
@@ -977,7 +1051,7 @@ class Product_Import extends Stepped_Job {
977
 
978
  if ( isset( $data['default_attributes'] ) && is_array( $data['default_attributes'] ) ) {
979
 
980
- $default_attributes = [];
981
 
982
  foreach ( $data['default_attributes'] as $default_attr_key => $default_attr ) {
983
 
@@ -1081,7 +1155,7 @@ class Product_Import extends Stepped_Job {
1081
 
1082
  if ( $date_to && ! $date_from ) {
1083
 
1084
- $date_from = strtotime( 'NOW', current_time( 'timestamp' ) );
1085
 
1086
  update_post_meta( $product_id, '_sale_price_dates_from', $date_from );
1087
  }
@@ -1096,12 +1170,12 @@ class Product_Import extends Stepped_Job {
1096
  update_post_meta( $product_id, '_price', $regular_price );
1097
  }
1098
 
1099
- if ( '' !== $sale_price && $date_from && strtotime( $date_from ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) {
1100
 
1101
  update_post_meta( $product_id, '_price', $sale_price );
1102
  }
1103
 
1104
- if ( $date_to && strtotime( $date_to ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) {
1105
 
1106
  update_post_meta( $product_id, '_price', $regular_price );
1107
  update_post_meta( $product_id, '_sale_price_dates_from', '' );
@@ -1124,11 +1198,13 @@ class Product_Import extends Stepped_Job {
1124
  }
1125
 
1126
  // Delete product attachments
1127
- $attachments = get_children( [
1128
- 'post_parent' => $product_id,
1129
- 'post_status' => 'any',
1130
- 'post_type' => 'attachment',
1131
- ] );
 
 
1132
 
1133
  foreach ( $attachments as $attachment ) {
1134
  wp_delete_attachment( $attachment->ID, true );
@@ -1138,5 +1214,90 @@ class Product_Import extends Stepped_Job {
1138
  wp_delete_post( $product_id, true );
1139
  }
1140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1141
 
1142
  }
30
  use WooCommerce\Square\Sync\Records\Record;
31
  use WooCommerce\Square\Utilities\Money_Utility;
32
 
33
+ defined( 'ABSPATH' ) || exit;
34
 
35
  /**
36
  * Class to represent a synchronization job to import products from Square.
42
 
43
  protected function assign_next_steps() {
44
 
45
+ $this->set_attr(
46
+ 'next_steps',
47
+ array(
48
+ 'import_products',
49
+ 'import_inventory',
50
+ )
51
+ );
52
  }
53
 
54
 
86
  */
87
  protected function import_products() {
88
 
89
+ $cursor = $this->get_attr( 'fetch_products_cursor' );
90
+ $imported_product_ids = $this->get_attr( 'processed_product_ids', array() );
91
+ $updated_product_ids = $this->get_attr( 'updated_product_ids', array() );
92
+ $skipped_products = $this->get_attr( 'skipped_products', array() );
93
 
94
+ $response = wc_square()->get_api()->search_catalog_objects(
95
+ array(
96
+ 'cursor' => $cursor,
97
+ 'object_types' => array( 'ITEM' ),
98
+ 'include_related_objects' => true,
99
+ 'limit' => $this->get_import_api_limit(),
100
+ )
101
+ );
102
 
103
  $related_objects = $response->get_data()->getRelatedObjects();
104
+ $categories = array();
105
 
106
  if ( $related_objects && is_array( $related_objects ) ) {
 
 
107
  foreach ( $related_objects as $related_object ) {
108
 
109
  if ( 'CATEGORY' === $related_object->getType() ) {
110
+ $categories[ $related_object->getId() ] = $related_object;
 
111
  }
112
  }
113
  }
123
  break;
124
  }
125
 
126
+ // validate permissions
127
+ if ( ! current_user_can( 'publish_products' ) ) {
128
+ $this->record_error( 'You do not have permission to create products' );
129
+ break; // use a break so we don't continue trying to import products without permissions
130
+ }
131
+
132
+ // validate Square Catalog object (API data)
133
+ if ( ! $catalog_object instanceof \SquareConnect\Model\CatalogObject || ! $catalog_object->getItemData() instanceof \SquareConnect\Model\CatalogItem ) {
134
+ $this->record_error( 'Invalid data' );
135
+ continue;
136
+ }
137
+
138
  $item_id = $catalog_object->getId();
139
 
140
+ // ignore items that have a missing SKU or are not available at our location
141
+ if ( ! $catalog_object->getPresentAtAllLocations() && ( ! is_array( $catalog_object->getPresentAtLocationIds() ) || ! in_array( wc_square()->get_settings_handler()->get_location_id(), $catalog_object->getPresentAtLocationIds(), true ) ) || $this->item_variation_has_missing_sku( $catalog_object ) ) {
142
 
143
  $skipped_products[ $item_id ] = null;
144
  continue;
145
  }
146
 
147
+ // import or update categories related to the products that are being imported
148
+ $catelog_category_id = $catalog_object->getItemData()->getCategoryId();
149
 
150
+ if ( $catelog_category_id && isset( $categories[ $catelog_category_id ] ) ) {
151
+ Category::import_or_update( $categories[ $catelog_category_id ] );
152
+ unset( $categories[ $catelog_category_id ] ); // don't import/update the same category multiple times per batch
153
  }
154
 
155
+ $product_id = Product::get_product_id_by_square_id( $item_id );
156
+ $product = ! empty( $product_id ) ? wc_get_product( $product_id ) : null;
157
+
158
+ if ( $product_id && ! $product ) {
159
+ $this->record_error( 'Product not found', $catalog_object, 'update' );
160
  continue;
161
  }
162
 
163
+ $data = $this->extract_product_data( $catalog_object, $product );
164
+
165
+ /**
166
+ * Filters the data that is used to create update a WooCommerce product during import.
167
+ *
168
+ * @since 2.0.0
169
+ *
170
+ * @param array $data product data
171
+ * @param \SquareConnect\Model\CatalogObject $catalog_object the catalog object from the Square API
172
+ * @param Product_Import $this import class instance
173
+ */
174
+ $data = apply_filters( 'woocommerce_square_create_product_data', $data, $catalog_object, $this );
175
 
176
+ // set default type
177
+ $data['type'] = ! empty( $data['type'] ) ? $data['type'] : 'simple';
178
 
179
+ // if an item matches an existing product, update the product using data from Square
180
+ if ( $product ) {
181
+ $product_id = $this->update_product( $product, $data );
182
 
183
+ if ( $product_id ) {
184
+ $updated_product_ids[] = $product_id;
185
+ }
186
+ } elseif ( $this->item_variation_has_matching_sku( $catalog_object ) ) {
187
+ // look in variation SKUs for a match - if so, skip the parent item, a normal sync should link it automatically
188
+ continue;
189
+ } else {
190
+ $product_id = $this->import_product( $data );
191
+
192
+ if ( $product_id ) {
193
+ Product::set_synced_with_square( $product_id );
194
+ $imported_product_ids[] = $product_id;
195
+ }
196
  }
197
  }
198
 
199
  if ( $time_exceeded ) {
200
 
201
+ wc_square()->log( '[Time Exceeded] Imported New Products Count: ' . count( $imported_product_ids ) );
202
 
203
  } else {
204
 
205
+ wc_square()->log( 'Imported New Products Count: ' . count( $imported_product_ids ) );
206
+ wc_square()->log( 'Updated Products Count: ' . count( $updated_product_ids ) );
207
 
208
  $cursor = $response->get_data()->getCursor();
209
  $this->set_attr( 'fetch_products_cursor', $cursor );
215
  }
216
 
217
  $this->set_attr( 'skipped_products', $skipped_products );
218
+ $this->set_attr( 'updated_product_ids', $updated_product_ids );
219
+ $this->set_attr( 'processed_product_ids', $imported_product_ids );
220
  }
221
 
222
 
229
  */
230
  protected function import_inventory() {
231
 
232
+ $search_result = wc_square()->get_api()->search_catalog_objects(
233
+ array(
234
+ 'object_types' => array( 'ITEM_VARIATION' ),
235
+ 'limit' => 100,
236
+ 'cursor' => $this->get_attr( 'import_inventory_cursor', null ),
237
+ )
238
+ );
239
 
240
  $cursor = $search_result->get_data()->getCursor();
241
+ $variation_ids = array_map(
242
+ static function( \SquareConnect\Model\CatalogObject $catalog_object ) {
243
+ return $catalog_object->getId();
244
+ },
245
+ $search_result->get_data()->getObjects()
246
+ );
247
+
248
+ $result = wc_square()->get_api()->batch_retrieve_inventory_counts(
249
+ array(
250
+ 'catalog_object_ids' => $variation_ids,
251
+ 'location_ids' => array( wc_square()->get_settings_handler()->get_location_id() ),
252
+ )
253
+ );
254
 
255
  foreach ( $result->get_counts() as $inventory_count ) {
256
 
276
  }
277
 
278
 
279
+ /**
280
+ * Determines whether any catalog item variation is missing a SKU
281
+ *
282
+ * @since 2.2.0
283
+ *
284
+ * @param \SquareConnect\Model\CatalogObject $catalog_object the catalog object
285
+ * @return bool
286
+ */
287
+ private function item_variation_has_missing_sku( $catalog_object ) {
288
+ $missing_sku = true;
289
+
290
+ foreach ( $catalog_object->getItemData()->getVariations() as $variation ) {
291
+ if ( in_array( trim( $variation->getItemVariationData()->getSku() ), array( '', null ), true ) ) { // can't use empty() because a valid SKU could be '0' which returns true
292
+ $missing_sku = true;
293
+ break;
294
+ }
295
+
296
+ $missing_sku = false;
297
+ }
298
+
299
+ return $missing_sku;
300
+ }
301
+
302
+
303
  /**
304
  * Determines whether a SKU within a catalog item is found in WooCommerce.
305
  *
323
 
324
 
325
  /**
326
+ * Creates a product from catalog object data.
327
  *
328
  * @since 2.0.0
329
  *
330
+ * @param array $data the catalog object data
331
  * @return int|null
332
  */
333
+ private function import_product( $data ) {
 
 
 
334
  try {
335
+ $product_id = $this->create_product_from_square_data( $data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
  // save product meta fields
338
  $this->save_product_meta( $product_id, $data );
360
 
361
  // clear cache/transients
362
  wc_delete_product_transients( $product_id );
 
363
  } catch ( Framework\SV_WC_Plugin_Exception $e ) {
364
+ // remove the product when creation fails
365
+ $this->clear_product( $product_id );
366
+ $product_id = 0;
367
 
368
+ $this->record_error( $e->getMessage(), $data );
369
+ }
370
 
371
+ return $product_id;
372
+ }
 
 
 
 
373
 
374
+ /**
375
+ * Updates a product from catalog object data
376
+ *
377
+ * @since 2.2.0
378
+ *
379
+ * @param WC_Product $product existing product in Woo that is being updated
380
+ * @param array $data the Square catalog object data
381
+ * @return int|null
382
+ */
383
+ private function update_product( $product, $data ) {
384
+ global $wpdb;
385
+ $product_id = $product->get_id();
386
 
387
+ try {
388
+ $wpdb->query( 'START TRANSACTION' );
389
+
390
+ // update an existing product from a simple to a variable product if it has at least two variations
391
+ if ( ! $product instanceof \WC_Product_Variable && 'variable' === $data['type'] ) {
392
+ $product_id = $this->update_simple_product_to_variable( $product_id, $data );
393
  }
394
 
395
+ // save product meta fields
396
+ $this->save_product_meta( $product_id, $data );
 
 
 
397
 
398
+ // save the image, if included
399
+ if ( ! empty( $data['image_id'] ) ) {
400
+ Product::update_image_from_square( $product_id, $data['image_id'] );
401
+ }
402
 
403
+ // save/update variations
404
+ if ( isset( $data['type'], $data['variations'] ) && 'variable' === $data['type'] && is_array( $data['variations'] ) ) {
405
+ $this->save_variations( $product_id, $data );
406
+ }
407
+
408
+ $wpdb->query( 'COMMIT' );
409
+
410
+ wc_delete_product_transients( $product_id );
411
+ } catch ( Framework\SV_WC_Plugin_Exception $e ) {
412
+ // undo any updated data when updating fails
413
+ $wpdb->query( 'ROLLBACK' );
414
  $product_id = 0;
415
+
416
+ $this->record_error( $e->getMessage(), $data, 'update' );
417
  }
418
 
419
  return $product_id;
420
  }
421
 
422
+ /**
423
+ * Convert an existing simple product to a variable when new variations are found.
424
+ * This function
425
+ *
426
+ * @since 2.2.0
427
+ *
428
+ * @param int $variation_id simple product ID being updated to a variation with new parent product
429
+ * @param array $data
430
+ * @return int|null
431
+ */
432
+ protected function update_simple_product_to_variable( $variation_id, $data = array() ) {
433
+ // create a new parent product
434
+ $parent_product_id = $this->create_product_from_square_data( $data );
435
+
436
+ // convert the simple product to a variation
437
+ wp_set_object_terms( $variation_id, 'variation', 'product_type' );
438
+ wp_update_post(
439
+ array(
440
+ 'ID' => $variation_id,
441
+ 'post_content' => '',
442
+ 'post_author' => get_current_user_id(),
443
+ 'post_parent' => $parent_product_id,
444
+ 'post_type' => 'product_variation',
445
+ )
446
+ );
447
+
448
+ // remove simple product meta that doesn't exist or needs be updated on the variation
449
+ delete_post_meta( $variation_id, Product::SQUARE_ID_META_KEY );
450
+ delete_post_meta( $variation_id, Product::SQUARE_VERSION_META_KEY );
451
+ delete_post_meta( $variation_id, Product::SQUARE_IMAGE_ID_META_KEY );
452
+ delete_post_meta( $variation_id, '_visibility' );
453
+
454
+ // copy total sales from previous simple product over to new parent variable product
455
+ $total_sales = get_post_meta( $variation_id, 'total_sales', true );
456
+
457
+ if ( $total_sales ) {
458
+ update_post_meta( $parent_product_id, 'total_sales', $total_sales );
459
+ }
460
+
461
+ return $parent_product_id;
462
+ }
463
 
464
  /**
465
  * Extracts product data from a CatalogObject to an array of data.
467
  * @since 2.0.0
468
  *
469
  * @param \SquareConnect\Model\CatalogObject $catalog_object the catalog object
470
+ * @param WC_Product|null $product
471
  * @return array|null
472
  * @throws Framework\SV_WC_Plugin_Exception
473
  */
474
+ protected function extract_product_data( $catalog_object, $product = null ) {
475
 
476
  $variations = $catalog_object->getItemData()->getVariations();
477
 
482
 
483
  $category_id = Category::get_category_id_by_square_id( $catalog_object->getItemData()->getCategoryId() );
484
 
485
+ $data = array(
486
  'title' => $catalog_object->getItemData()->getName(),
487
+ 'type' => ( 1 === count( $variations ) && ! ( $product && $product instanceof \WC_Product_Variable ) ) ? 'simple' : 'variable',
488
+ 'sku' => '', // make sure to reset SKU when simple product is updated to variable
489
  'description' => $catalog_object->getItemData()->getDescription(),
490
  'image_id' => $catalog_object->getImageId(),
491
+ 'categories' => array( $category_id ),
492
+ 'square_meta' => array(
493
  'item_id' => $catalog_object->getId(),
494
  'item_version' => $catalog_object->getVersion(),
495
+ ),
496
+ );
497
 
498
  // variable product
499
+ if ( 'variable' === $data['type'] ) {
500
+ $data['variations'] = array();
 
 
501
 
502
  foreach ( $variations as $variation ) {
503
 
513
  } catch ( Framework\SV_WC_Plugin_Exception $exception ) {
514
 
515
  // alert for failed variation imports
516
+ Records::set_record(
517
+ array(
518
+ 'type' => 'alert',
519
+ 'message' => sprintf(
520
+ /* translators: Placeholders: %1$s - Square item name, %2$s - Square item variation name, %3$s - failure reason */
521
+ __( 'Could not import "%1$s - %2$s" from Square. %3$s', 'woocommerce-square' ),
522
+ $catalog_object->getItemData()->getName(),
523
+ $variation->getItemVariationData()->getName(),
524
+ $exception->getMessage()
525
+ ),
526
+ )
527
+ );
528
  }
529
  }
530
 
531
+ $data['attributes'] = array(
532
+ array(
533
+ 'name' => 'Attribute',
534
+ 'slug' => 'attribute',
535
+ 'position' => 0,
536
+ 'visible' => true,
537
+ 'variation' => true,
538
+ 'options' => wp_list_pluck( $data['variations'], 'name' ),
539
+ ),
540
+ );
541
+ } else { // simple product
 
542
  $variation = $this->extract_square_item_variation_data( $variations[0] );
543
 
544
  $data['type'] = 'simple';
545
+ $data['sku'] = ! empty( $variation['sku'] ) ? $variation['sku'] : null;
546
+ $data['regular_price'] = ! empty( $variation['regular_price'] ) ? $variation['regular_price'] : null;
547
+ $data['stock_quantity'] = ! empty( $variation['stock_quantity'] ) ? $variation['stock_quantity'] : null;
548
+ $data['managing_stock'] = ! empty( $variation['managing_stock'] ) ? $variation['managing_stock'] : null;
549
 
550
+ $data['square_meta']['item_variation_id'] = ! empty( $variation['square_meta']['item_variation_id'] ) ? $variation['square_meta']['item_variation_id'] : null;
551
+ $data['square_meta']['item_variation_version'] = ! empty( $variation['square_meta']['item_variation_version'] ) ? $variation['square_meta']['item_variation_version'] : null;
552
  }
553
 
554
  return $data;
572
  throw new Framework\SV_WC_Plugin_Exception( __( 'Items with variable pricing cannot be imported.', 'woocommerce-square' ) );
573
  }
574
 
575
+ $data = array(
576
+ 'name' => $variation_data->getName(),
577
+ 'sku' => $variation_data->getSku(),
578
  'regular_price' => $variation_data->getPriceMoney() && $variation_data->getPriceMoney()->getAmount() ? Money_Utility::cents_to_float( $variation->getItemVariationData()->getPriceMoney()->getAmount() ) : null,
579
  'stock_quantity' => null,
580
  'managing_stock' => true,
581
+ 'square_meta' => array(
582
  'item_variation_id' => $variation->getId(),
583
  'item_variation_version' => $variation->getVersion(),
584
+ ),
585
+ 'attributes' => array(
586
+ array(
587
  'name' => 'Attribute',
588
  'is_variation' => true,
589
+ 'option' => $variation_data->getName(),
590
+ ),
591
+ ),
592
+ );
593
 
594
  return $data;
595
  }
624
  $product_type = wc_clean( $data['type'] );
625
 
626
  wp_set_object_terms( $product_id, $product_type, 'product_type' );
 
627
  } else {
 
628
  $_product_type = get_the_terms( $product_id, 'product_type' );
629
 
630
  if ( is_array( $_product_type ) ) {
638
  add_post_meta( $product_id, 'total_sales', '0', true );
639
 
640
  // catalog visibility
641
+ update_post_meta( $product_id, '_visibility', ! empty( $data['catalog_visibility'] ) ? wc_clean( $data['catalog_visibility'] ) : 'visible' );
 
 
 
642
 
643
  // sku
644
  if ( isset( $data['sku'] ) ) {
664
 
665
  throw new Framework\SV_WC_Plugin_Exception( __( 'The SKU already exists on another product', 'woocommerce-square' ) );
666
  }
 
667
  } else {
 
668
  update_post_meta( $product_id, '_sku', '' );
669
  }
670
  }
673
  // attributes
674
  if ( isset( $data['attributes'] ) ) {
675
 
676
+ $attributes = array();
677
 
678
  foreach ( $data['attributes'] as $attribute ) {
679
 
714
 
715
  } else {
716
 
717
+ $values = array();
718
  }
719
 
720
  // update post terms
726
  if ( ! empty( $values ) ) {
727
 
728
  // add attribute to array, but don't set values
729
+ $attributes[ $taxonomy ] = array(
730
  'name' => $taxonomy,
731
  'value' => '',
732
  'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0',
733
  'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0,
734
  'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0,
735
  'is_taxonomy' => $is_taxonomy,
736
+ );
737
  }
 
738
  } elseif ( isset( $attribute['options'] ) ) {
 
739
  // array based
740
  if ( is_array( $attribute['options'] ) ) {
741
 
747
  }
748
 
749
  // custom attribute - add attribute to array and set the values
750
+ $attributes[ $attribute_slug ] = array(
751
  'name' => wc_clean( $attribute['name'] ),
752
  'value' => $values,
753
  'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0',
754
  'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0,
755
  'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0,
756
  'is_taxonomy' => $is_taxonomy,
757
+ );
758
  }
759
  }
760
 
764
  }
765
 
766
  // sales and prices
767
+ if ( in_array( $product_type, array( 'variable', 'grouped' ), true ) ) {
768
 
769
  // variable and grouped products have no prices
770
  update_post_meta( $product_id, '_regular_price', '' );
833
  protected function save_variations( $product_id, $data ) {
834
  global $wpdb;
835
 
836
+ $variations = $data['variations'];
837
+ $attributes = (array) maybe_unserialize( get_post_meta( $product_id, '_product_attributes', true ) );
838
+ $variable_product = wc_get_product( $product_id );
839
+ $variations_to_remove = $variable_product ? $variable_product->get_children() : array();
840
 
841
  foreach ( $variations as $menu_order => $variation ) {
842
 
848
  $variation_id = wc_get_product_id_by_sku( $variation_sku );
849
  }
850
 
851
+ /* translators: Placeholders: %1$s - variation ID, %2$s - product name */
852
  $variation_post_title = sprintf( __( 'Variation #%1$s of %2$s', 'woocommerce-square' ), $variation_id, esc_html( get_the_title( $product_id ) ) );
853
 
854
  // update or add post
855
  if ( ! $variation_id ) {
856
 
857
  $post_status = ( isset( $variation['visible'] ) && false === $variation['visible'] ) ? 'private' : 'publish';
858
+ $new_variation = array(
859
  'post_title' => $variation_post_title,
860
  'post_content' => '',
861
  'post_status' => $post_status,
863
  'post_parent' => $product_id,
864
  'post_type' => 'product_variation',
865
  'menu_order' => $menu_order,
866
+ );
867
 
868
  $variation_id = wp_insert_post( $new_variation );
869
 
878
 
879
  } else {
880
 
881
+ $update_variation = array(
882
+ 'post_title' => $variation_post_title,
883
+ 'menu_order' => $menu_order,
884
+ );
885
 
886
  if ( isset( $variation['visible'] ) ) {
887
 
890
  $update_variation['post_status'] = $post_status;
891
  }
892
 
893
+ $wpdb->update( $wpdb->posts, $update_variation, array( 'ID' => $variation_id ) );
894
+
895
+ // remove any variation from $variations_to_remove that is found in Sqaure and also matches a variation in Woo
896
+ if ( ( $key = array_search( $variation_id, $variations_to_remove, true ) ) !== false ) { //phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found, Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
897
+ unset( $variations_to_remove[ $key ] );
898
+ }
899
 
900
  /**
901
  * Fired after updating a product variation during an import from Square.
935
 
936
  throw new Framework\SV_WC_Plugin_Exception( __( 'The SKU already exists on another product', 'woocommerce-square' ) );
937
  }
 
938
  } else {
939
 
940
  update_post_meta( $variation_id, '_sku', '' );
943
  }
944
 
945
  update_post_meta( $variation_id, '_manage_stock', 'yes' );
946
+ update_post_meta( $variation_id, '_backorders', 'no' );
947
 
948
  $this->wc_save_product_price( $variation_id, $variation['regular_price'] );
949
 
959
  // update taxonomies
960
  if ( isset( $variation['attributes'] ) ) {
961
 
962
+ $updated_attribute_keys = array();
963
 
964
  foreach ( $variation['attributes'] as $attribute_key => $attribute ) {
965
 
968
  }
969
 
970
  $taxonomy = 0;
971
+ $_attribute = array();
972
 
973
  if ( isset( $attribute['slug'] ) ) {
974
 
1005
  }
1006
 
1007
  // remove old taxonomies attributes so data is kept up to date - first get attribute key names
1008
+ $delete_attribute_keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->postmeta} WHERE meta_key LIKE 'attribute_%%' AND meta_key NOT IN ( '" . implode( "','", $updated_attribute_keys ) . "' ) AND post_id = %d;", $variation_id ) ); //phpcs:ignore WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.PreparedSQL.NotPrepared
1009
 
1010
  foreach ( $delete_attribute_keys as $key ) {
1011
 
1035
  do_action( 'woocommerce_square_save_product_variation', $variation_id, $menu_order, $variation );
1036
  }
1037
 
1038
+ // remove any existing variations on the product that no longer exist in Square
1039
+ foreach ( $variations_to_remove as $variation_id ) {
1040
+ wp_delete_post( $variation_id, true );
1041
+ }
1042
+
1043
  // update parent if variable so price sorting works and stays in sync with the cheapest child
1044
  \WC_Product_Variable::sync( $product_id );
1045
 
1051
 
1052
  if ( isset( $data['default_attributes'] ) && is_array( $data['default_attributes'] ) ) {
1053
 
1054
+ $default_attributes = array();
1055
 
1056
  foreach ( $data['default_attributes'] as $default_attr_key => $default_attr ) {
1057
 
1155
 
1156
  if ( $date_to && ! $date_from ) {
1157
 
1158
+ $date_from = strtotime( 'NOW', current_time( 'timestamp' ) ); //phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
1159
 
1160
  update_post_meta( $product_id, '_sale_price_dates_from', $date_from );
1161
  }
1170
  update_post_meta( $product_id, '_price', $regular_price );
1171
  }
1172
 
1173
+ if ( '' !== $sale_price && $date_from && strtotime( $date_from ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { //phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
1174
 
1175
  update_post_meta( $product_id, '_price', $sale_price );
1176
  }
1177
 
1178
+ if ( $date_to && strtotime( $date_to ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { //phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
1179
 
1180
  update_post_meta( $product_id, '_price', $regular_price );
1181
  update_post_meta( $product_id, '_sale_price_dates_from', '' );
1198
  }
1199
 
1200
  // Delete product attachments
1201
+ $attachments = get_children(
1202
+ array(
1203
+ 'post_parent' => $product_id,
1204
+ 'post_status' => 'any',
1205
+ 'post_type' => 'attachment',
1206
+ )
1207
+ );
1208
 
1209
  foreach ( $attachments as $attachment ) {
1210
  wp_delete_attachment( $attachment->ID, true );
1214
  wp_delete_post( $product_id, true );
1215
  }
1216
 
1217
+ /**
1218
+ * Creates a product in WooCommerce using the data from Square
1219
+ *
1220
+ * @since 2.2.0
1221
+ *
1222
+ * @param array $data New product data
1223
+ * @return int
1224
+ * @throws Framework\SV_WC_Plugin_Exception
1225
+ */
1226
+ protected function create_product_from_square_data( $data = array() ) {
1227
+ // validate title field
1228
+ if ( ! isset( $data['title'] ) ) {
1229
+ /* translators: Placeholders: %s - missing parameter name */
1230
+ throw new Framework\SV_WC_Plugin_Exception( sprintf( __( 'Missing parameter %s', 'woocommerce-square' ), 'title' ) );
1231
+ }
1232
+
1233
+ // validate type
1234
+ if ( ! array_key_exists( wc_clean( $data['type'] ), wc_get_product_types() ) ) {
1235
+ /* translators: Placeholders: %s - comma separated list of valid product types */
1236
+ throw new Framework\SV_WC_Plugin_Exception( sprintf( __( 'Invalid product type - the product type must be any of these: %s', 'woocommerce-square' ), implode( ', ', array_keys( wc_get_product_types() ) ) ) );
1237
+ }
1238
+
1239
+ // set description
1240
+ $post_content = isset( $data['description'] ) ? wc_clean( $data['description'] ) : '';
1241
+
1242
+ if ( $post_content && isset( $data['enable_html_description'] ) && true === $data['enable_html_description'] ) {
1243
+ $post_content = $data['description'];
1244
+ }
1245
+
1246
+ $new_product = array(
1247
+ 'post_title' => wc_clean( $data['title'] ),
1248
+ 'post_status' => isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish',
1249
+ 'post_type' => 'product',
1250
+ 'post_content' => isset( $data['description'] ) ? $post_content : '',
1251
+ 'post_author' => get_current_user_id(),
1252
+ 'menu_order' => isset( $data['menu_order'] ) ? (int) $data['menu_order'] : 0,
1253
+ );
1254
+
1255
+ if ( ! empty( $data['name'] ) ) {
1256
+ $new_product = array_merge( $new_product, array( 'post_name' => sanitize_title( $data['name'] ) ) );
1257
+ }
1258
+
1259
+ // attempt to create the new product
1260
+ $product_id = wp_insert_post( $new_product, true );
1261
+
1262
+ if ( is_wp_error( $product_id ) ) {
1263
+ throw new Framework\SV_WC_Plugin_Exception( $product_id->get_error_message() );
1264
+ }
1265
+
1266
+ return $product_id;
1267
+ }
1268
+
1269
+ /**
1270
+ * Record an error made during import or update by creating a new Sync Record and Square log
1271
+ *
1272
+ * @since 2.2.0
1273
+ *
1274
+ * @param string $error Error message to record
1275
+ * @param \SquareConnect\Model\CatalogObject|array|null $catalog_item the catalog object or data
1276
+ * @param string $context Context for whether the error occurred during import or update
1277
+ */
1278
+ protected function record_error( $error, $catalog_item = null, $context = 'import' ) {
1279
+ if ( $catalog_item && $catalog_item instanceof \SquareConnect\Model\CatalogObject && $catalog_item->getItemData() instanceof \SquareConnect\Model\CatalogItem ) {
1280
+ $item_name = $catalog_item->getItemData()->getName();
1281
+ } elseif ( is_array( $catalog_item ) && ! empty( $catalog_item['title'] ) ) {
1282
+ $item_name = $catalog_item['title'];
1283
+ }
1284
+
1285
+ if ( 'update' === $context ) {
1286
+ /* translators: Placeholders: %1$s - Square item name, %2$s - Failure reason */
1287
+ $message = sprintf( __( 'Could not update %1$s from Square. %2$s', 'woocommerce-square' ), ! empty( $item_name ) ? '"' . $item_name . '"' : 'item', $error );
1288
+ } else {
1289
+ /* translators: Placeholders: %1$s - Square item name, %2$s - Failure reason */
1290
+ $message = sprintf( __( 'Could not import %1$s from Square. %2$s', 'woocommerce-square' ), ! empty( $item_name ) ? '"' . $item_name . '"' : 'item', $error );
1291
+ }
1292
+
1293
+ Records::set_record(
1294
+ array(
1295
+ 'type' => 'alert',
1296
+ 'message' => $message,
1297
+ )
1298
+ );
1299
+
1300
+ wc_square()->log( sprintf( 'Error %s product during import: %s', 'import' === $context ? 'creating' : 'updating', $error ) );
1301
+ }
1302
 
1303
  }
includes/Sync/Records.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Sync;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Sync\Records\Record;
@@ -63,7 +63,7 @@ class Records {
63
  */
64
  public static function get_record( $id ) {
65
 
66
- $records = self::get_records( [ 'id' => $id ] );
67
 
68
  return ! empty( $records ) ? current( $records ) : null;
69
  }
@@ -77,19 +77,22 @@ class Records {
77
  * @param array $args associative array of arguments to query records
78
  * @return Record[]
79
  */
80
- public static function get_records( array $args = [] ) {
81
-
82
- $args = wp_parse_args( $args, [
83
- 'id' => null,
84
- 'type' => null,
85
- 'product' => null,
86
- 'orderby' => 'date',
87
- 'sort' => 'DESC',
88
- 'limit' => 50,
89
- ] );
90
-
91
- $records = [];
92
- $raw_records = get_option( self::$records_option_key, [] );
 
 
 
93
 
94
  foreach ( $raw_records as $raw_record_data ) {
95
 
@@ -128,12 +131,12 @@ class Records {
128
  if ( ! empty( $records ) ) {
129
 
130
  switch ( $args['orderby'] ) {
131
- case 'date' :
132
- uasort( $records, [ 'self', 'sort_records_by_date' ] );
133
- break;
134
- case 'type' :
135
- uasort( $records, [ 'self', 'sort_records_by_type' ] );
136
- break;
137
  }
138
 
139
  if ( 'DESC' === $args['sort'] ) {
@@ -240,8 +243,8 @@ class Records {
240
  if ( $new_record instanceof Record ) {
241
 
242
  // ensures there are never more than 50 records, leaving behind the older ones
243
- $existing_records = self::get_records( [ 'limit' => 49 ] );
244
- $raw_records = [];
245
 
246
  foreach ( $existing_records as $existing_record ) {
247
  $raw_records[ $existing_record->get_id() ] = $existing_record->get_data();
@@ -251,7 +254,7 @@ class Records {
251
 
252
  $success = update_option( self::$records_option_key, $raw_records );
253
 
254
- } else {
255
 
256
  $success = false;
257
  }
@@ -271,7 +274,7 @@ class Records {
271
  public static function set_records( array $data ) {
272
 
273
  $success = false;
274
- $raw_records = [];
275
 
276
  foreach ( $data as $record ) {
277
 
@@ -286,7 +289,7 @@ class Records {
286
 
287
  if ( ! empty( $raw_records ) ) {
288
 
289
- $records = self::get_records( [ 'limit' => 50 - count( $raw_records ) ] );
290
 
291
  foreach ( $records as $record ) {
292
  $raw_records[ $record->get_id() ] = $record->get_data();
@@ -310,7 +313,7 @@ class Records {
310
  public static function delete_record( $id ) {
311
 
312
  $success = false;
313
- $raw_records = get_option( self::$records_option_key, [] );
314
 
315
  if ( array_key_exists( $id, $raw_records ) ) {
316
 
@@ -334,9 +337,9 @@ class Records {
334
  public static function delete_records( array $args ) {
335
 
336
  $removed = 0;
337
- $raw_records = get_option( self::$records_option_key, [] );
338
 
339
- foreach( $raw_records as $raw_record ) {
340
 
341
  $record = new Record( $raw_record );
342
 
@@ -383,7 +386,7 @@ class Records {
383
  */
384
  public static function clean_records() {
385
 
386
- return update_option( self::$records_option_key, [] );
387
  }
388
 
389
 
23
 
24
  namespace WooCommerce\Square\Sync;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Sync\Records\Record;
63
  */
64
  public static function get_record( $id ) {
65
 
66
+ $records = self::get_records( array( 'id' => $id ) );
67
 
68
  return ! empty( $records ) ? current( $records ) : null;
69
  }
77
  * @param array $args associative array of arguments to query records
78
  * @return Record[]
79
  */
80
+ public static function get_records( array $args = array() ) {
81
+
82
+ $args = wp_parse_args(
83
+ $args,
84
+ array(
85
+ 'id' => null,
86
+ 'type' => null,
87
+ 'product' => null,
88
+ 'orderby' => 'date',
89
+ 'sort' => 'DESC',
90
+ 'limit' => 50,
91
+ )
92
+ );
93
+
94
+ $records = array();
95
+ $raw_records = get_option( self::$records_option_key, array() );
96
 
97
  foreach ( $raw_records as $raw_record_data ) {
98
 
131
  if ( ! empty( $records ) ) {
132
 
133
  switch ( $args['orderby'] ) {
134
+ case 'date':
135
+ uasort( $records, array( 'self', 'sort_records_by_date' ) );
136
+ break;
137
+ case 'type':
138
+ uasort( $records, array( 'self', 'sort_records_by_type' ) );
139
+ break;
140
  }
141
 
142
  if ( 'DESC' === $args['sort'] ) {
243
  if ( $new_record instanceof Record ) {
244
 
245
  // ensures there are never more than 50 records, leaving behind the older ones
246
+ $existing_records = self::get_records( array( 'limit' => 49 ) );
247
+ $raw_records = array();
248
 
249
  foreach ( $existing_records as $existing_record ) {
250
  $raw_records[ $existing_record->get_id() ] = $existing_record->get_data();
254
 
255
  $success = update_option( self::$records_option_key, $raw_records );
256
 
257
+ } else {
258
 
259
  $success = false;
260
  }
274
  public static function set_records( array $data ) {
275
 
276
  $success = false;
277
+ $raw_records = array();
278
 
279
  foreach ( $data as $record ) {
280
 
289
 
290
  if ( ! empty( $raw_records ) ) {
291
 
292
+ $records = self::get_records( array( 'limit' => 50 - count( $raw_records ) ) );
293
 
294
  foreach ( $records as $record ) {
295
  $raw_records[ $record->get_id() ] = $record->get_data();
313
  public static function delete_record( $id ) {
314
 
315
  $success = false;
316
+ $raw_records = get_option( self::$records_option_key, array() );
317
 
318
  if ( array_key_exists( $id, $raw_records ) ) {
319
 
337
  public static function delete_records( array $args ) {
338
 
339
  $removed = 0;
340
+ $raw_records = get_option( self::$records_option_key, array() );
341
 
342
+ foreach ( $raw_records as $raw_record ) {
343
 
344
  $record = new Record( $raw_record );
345
 
386
  */
387
  public static function clean_records() {
388
 
389
+ return update_option( self::$records_option_key, array() );
390
  }
391
 
392
 
includes/Sync/Records/Record.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Sync\Records;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Sync\Records;
@@ -81,20 +81,23 @@ class Record {
81
  private function parse_data( $data ) {
82
 
83
  if ( is_numeric( $data ) ) {
84
- $data = [
85
  'type' => 'alert',
86
  'product_id' => absint( $data ),
87
- ];
88
  }
89
 
90
  $date = date( 'Y-m-d H:i:s', current_time( 'timestamp', true ) );
91
- $data = wp_parse_args( (array) $data, [
92
- 'type' => $this->get_default_type(),
93
- 'date' => $date,
94
- 'message' => '',
95
- 'product_id' => 0,
96
- 'product_hidden' => false,
97
- ] );
 
 
 
98
 
99
  if ( empty( $data['id'] ) ) {
100
  $data['id'] = uniqid( 'wc_square_sync_record_', false );
@@ -129,14 +132,14 @@ class Record {
129
  */
130
  public function get_data() {
131
 
132
- return [
133
  'id' => (string) $this->id,
134
  'type' => (string) $this->type,
135
  'date' => (string) $this->date,
136
  'message' => (string) $this->message,
137
  'product_id' => (int) $this->product_id,
138
  'product_hidden' => (bool) $this->product_hidden,
139
- ];
140
  }
141
 
142
 
@@ -199,12 +202,12 @@ class Record {
199
  */
200
  private function get_valid_types() {
201
 
202
- return [
203
  'info' => __( 'Info', 'woocommerce-square' ),
204
  'notice' => __( 'Notice', 'woocommerce-square' ),
205
  'alert' => __( 'Alert', 'woocommerce-square' ),
206
  'resolved' => __( 'Resolved', 'woocommerce-square' ),
207
- ];
208
  }
209
 
210
 
@@ -502,7 +505,7 @@ class Record {
502
 
503
  if ( is_bool( $was_hidden ) ) {
504
  $this->product_hidden = $was_hidden;
505
- $set = true;
506
  }
507
 
508
  return $set;
@@ -533,32 +536,32 @@ class Record {
533
  */
534
  public function get_actions() {
535
 
536
- $actions = [];
537
 
538
  if ( ! $this->is_resolved() ) {
539
 
540
- $actions['delete'] = (object) [
541
  'name' => 'delete',
542
  'label' => __( 'Delete', 'woocommerce-square' ),
543
  'icon' => '<span class="dashicons dashicons-trash"></span>',
544
- ];
545
 
546
  if ( ! $this->is_type( 'info' ) ) {
547
 
548
- $actions['resolve'] = (object) [
549
  'name' => 'resolve',
550
  'label' => __( 'Ignore', 'woocommerce-square' ),
551
  'icon' => '<span class="dashicons dashicons-hidden"></span>',
552
- ];
553
  }
554
 
555
  if ( $this->has_product() ) {
556
 
557
- $actions['unsync'] = (object) [
558
  'name' => 'unsync',
559
  'label' => __( 'Unlink', 'woocommerce-square' ),
560
  'icon' => '<span class="dashicons dashicons-editor-unlink"></span>',
561
- ];
562
  }
563
  }
564
 
23
 
24
  namespace WooCommerce\Square\Sync\Records;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
  use WooCommerce\Square\Sync\Records;
81
  private function parse_data( $data ) {
82
 
83
  if ( is_numeric( $data ) ) {
84
+ $data = array(
85
  'type' => 'alert',
86
  'product_id' => absint( $data ),
87
+ );
88
  }
89
 
90
  $date = date( 'Y-m-d H:i:s', current_time( 'timestamp', true ) );
91
+ $data = wp_parse_args(
92
+ (array) $data,
93
+ array(
94
+ 'type' => $this->get_default_type(),
95
+ 'date' => $date,
96
+ 'message' => '',
97
+ 'product_id' => 0,
98
+ 'product_hidden' => false,
99
+ )
100
+ );
101
 
102
  if ( empty( $data['id'] ) ) {
103
  $data['id'] = uniqid( 'wc_square_sync_record_', false );
132
  */
133
  public function get_data() {
134
 
135
+ return array(
136
  'id' => (string) $this->id,
137
  'type' => (string) $this->type,
138
  'date' => (string) $this->date,
139
  'message' => (string) $this->message,
140
  'product_id' => (int) $this->product_id,
141
  'product_hidden' => (bool) $this->product_hidden,
142
+ );
143
  }
144
 
145
 
202
  */
203
  private function get_valid_types() {
204
 
205
+ return array(
206
  'info' => __( 'Info', 'woocommerce-square' ),
207
  'notice' => __( 'Notice', 'woocommerce-square' ),
208
  'alert' => __( 'Alert', 'woocommerce-square' ),
209
  'resolved' => __( 'Resolved', 'woocommerce-square' ),
210
+ );
211
  }
212
 
213
 
505
 
506
  if ( is_bool( $was_hidden ) ) {
507
  $this->product_hidden = $was_hidden;
508
+ $set = true;
509
  }
510
 
511
  return $set;
536
  */
537
  public function get_actions() {
538
 
539
+ $actions = array();
540
 
541
  if ( ! $this->is_resolved() ) {
542
 
543
+ $actions['delete'] = (object) array(
544
  'name' => 'delete',
545
  'label' => __( 'Delete', 'woocommerce-square' ),
546
  'icon' => '<span class="dashicons dashicons-trash"></span>',
547
+ );
548
 
549
  if ( ! $this->is_type( 'info' ) ) {
550
 
551
+ $actions['resolve'] = (object) array(
552
  'name' => 'resolve',
553
  'label' => __( 'Ignore', 'woocommerce-square' ),
554
  'icon' => '<span class="dashicons dashicons-hidden"></span>',
555
+ );
556
  }
557
 
558
  if ( $this->has_product() ) {
559
 
560
+ $actions['unsync'] = (object) array(
561
  'name' => 'unsync',
562
  'label' => __( 'Unlink', 'woocommerce-square' ),
563
  'icon' => '<span class="dashicons dashicons-editor-unlink"></span>',
564
+ );
565
  }
566
  }
567
 
includes/Sync/Stepped_Job.php CHANGED
@@ -25,7 +25,7 @@ namespace WooCommerce\Square\Sync;
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
- defined( 'ABSPATH' ) or exit;
29
 
30
  /**
31
  * Stepped Job abstract.
@@ -105,7 +105,7 @@ abstract class Stepped_Job extends Job {
105
 
106
  $next_step = $this->get_next_step();
107
 
108
- if ( is_callable( [ $this, $next_step ] ) ) {
109
 
110
  $this->start_step_cycle( $next_step );
111
 
@@ -138,10 +138,10 @@ abstract class Stepped_Job extends Job {
138
  */
139
  protected function start_step_cycle( $step_name ) {
140
 
141
- $current_step_cycle = [
142
  'step_name' => $step_name,
143
  'start_time' => microtime( true ),
144
- ];
145
 
146
  wc_square()->log( "Starting step cycle: $step_name" );
147
 
@@ -160,7 +160,7 @@ abstract class Stepped_Job extends Job {
160
  */
161
  protected function complete_step_cycle( $step_name, $is_successful = true, $error_message = '' ) {
162
 
163
- $current_step_cycle = $this->get_attr( 'current_step_cycle', [] );
164
 
165
  if ( ! empty( $current_step_cycle ) ) {
166
 
@@ -177,7 +177,7 @@ abstract class Stepped_Job extends Job {
177
  wc_square()->log( "Failed step cycle: $step_name (${current_step_cycle['runtime']}) - $error_message" );
178
  }
179
 
180
- $completed_cycles = $this->get_attr( 'completed_step_cycles', [] );
181
  $completed_cycles[] = $current_step_cycle;
182
  $this->set_attr( 'completed_step_cycles', $completed_cycles );
183
  }
@@ -217,12 +217,12 @@ abstract class Stepped_Job extends Job {
217
  return;
218
  }
219
 
220
- $completed_steps = $this->get_attr( 'completed_steps', [] );
221
 
222
- $completed_steps[] = [
223
  'name' => $step_name,
224
  'completion_time' => current_time( 'mysql' ),
225
- ];
226
 
227
  $this->set_attr( 'completed_steps', $completed_steps );
228
 
25
 
26
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
27
 
28
+ defined( 'ABSPATH' ) || exit;
29
 
30
  /**
31
  * Stepped Job abstract.
105
 
106
  $next_step = $this->get_next_step();
107
 
108
+ if ( is_callable( array( $this, $next_step ) ) ) {
109
 
110
  $this->start_step_cycle( $next_step );
111
 
138
  */
139
  protected function start_step_cycle( $step_name ) {
140
 
141
+ $current_step_cycle = array(
142
  'step_name' => $step_name,
143
  'start_time' => microtime( true ),
144
+ );
145
 
146
  wc_square()->log( "Starting step cycle: $step_name" );
147
 
160
  */
161
  protected function complete_step_cycle( $step_name, $is_successful = true, $error_message = '' ) {
162
 
163
+ $current_step_cycle = $this->get_attr( 'current_step_cycle', array() );
164
 
165
  if ( ! empty( $current_step_cycle ) ) {
166
 
177
  wc_square()->log( "Failed step cycle: $step_name (${current_step_cycle['runtime']}) - $error_message" );
178
  }
179
 
180
+ $completed_cycles = $this->get_attr( 'completed_step_cycles', array() );
181
  $completed_cycles[] = $current_step_cycle;
182
  $this->set_attr( 'completed_step_cycles', $completed_cycles );
183
  }
217
  return;
218
  }
219
 
220
+ $completed_steps = $this->get_attr( 'completed_steps', array() );
221
 
222
+ $completed_steps[] = array(
223
  'name' => $step_name,
224
  'completion_time' => current_time( 'mysql' ),
225
+ );
226
 
227
  $this->set_attr( 'completed_steps', $completed_steps );
228
 
includes/Utilities/Encryption_Utility.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Utilities;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -67,8 +67,7 @@ class Encryption_Utility {
67
 
68
  $this->cipher_method = $preferred_cipher_method;
69
 
70
- // otherwise, throw a notice and continue with the default
71
- } else {
72
 
73
  $message = sprintf(
74
  __( '%1$s encryption is not available on this site. %2$s will be used instead.', 'woocommerce-square' ),
23
 
24
  namespace WooCommerce\Square\Utilities;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
67
 
68
  $this->cipher_method = $preferred_cipher_method;
69
 
70
+ } else { // otherwise, throw a notice and continue with the default
 
71
 
72
  $message = sprintf(
73
  __( '%1$s encryption is not available on this site. %2$s will be used instead.', 'woocommerce-square' ),
includes/Utilities/Money_Utility.php CHANGED
@@ -23,7 +23,7 @@
23
 
24
  namespace WooCommerce\Square\Utilities;
25
 
26
- defined( 'ABSPATH' ) or exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
@@ -49,10 +49,12 @@ class Money_Utility {
49
  */
50
  public static function amount_to_money( $amount, $currency ) {
51
 
52
- return new \SquareConnect\Model\Money( [
53
- 'amount' => self::amount_to_cents( $amount, $currency ),
54
- 'currency' => $currency,
55
- ] );
 
 
56
  }
57
 
58
 
@@ -106,7 +108,7 @@ class Money_Utility {
106
  */
107
  public static function get_currency_decimals( $currency ) {
108
 
109
- $other_currencies = [
110
  'BIF' => 0,
111
  'CLP' => 0,
112
  'DJF' => 0,
@@ -123,7 +125,7 @@ class Money_Utility {
123
  'XAF' => 0,
124
  'XOF' => 0,
125
  'XPF' => 0,
126
- ];
127
 
128
  if ( Framework\SV_WC_Plugin_Compatibility::is_wc_version_gte( '3.5' ) ) {
129
 
@@ -136,7 +138,7 @@ class Money_Utility {
136
 
137
  } else {
138
 
139
- $currencies = [];
140
  $currency_codes = get_woocommerce_currencies();
141
 
142
  foreach ( $currency_codes as $code => $name ) {
23
 
24
  namespace WooCommerce\Square\Utilities;
25
 
26
+ defined( 'ABSPATH' ) || exit;
27
 
28
  use SkyVerge\WooCommerce\PluginFramework\v5_4_0 as Framework;
29
 
49
  */
50
  public static function amount_to_money( $amount, $currency ) {
51
 
52
+ return new \SquareConnect\Model\Money(
53
+ array(
54
+ 'amount' => self::amount_to_cents( $amount, $currency ),
55
+ 'currency' => $currency,
56
+ )
57
+ );
58
  }
59
 
60
 
108
  */
109
  public static function get_currency_decimals( $currency ) {
110
 
111
+ $other_currencies = array(
112
  'BIF' => 0,
113
  'CLP' => 0,
114
  'DJF' => 0,
125
  'XAF' => 0,
126
  'XOF' => 0,
127
  'XPF' => 0,
128
+ );
129
 
130
  if ( Framework\SV_WC_Plugin_Compatibility::is_wc_version_gte( '3.5' ) ) {
131
 
138
 
139
  } else {
140
 
141
+ $currencies = array();
142
  $currency_codes = get_woocommerce_currencies();
143
 
144
  foreach ( $currency_codes as $code => $name ) {
includes/Utilities/String_Utility.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WooCommerce Square
4
+ *
5
+ * This source file is subject to the GNU General Public License v3.0
6
+ * that is bundled with this package in the file license.txt.
7
+ * It is also available through the world-wide-web at this URL:
8
+ * http://www.gnu.org/licenses/gpl-3.0.html
9
+ * If you did not receive a copy of the license and are unable to
10
+ * obtain it through the world-wide-web, please send an email
11
+ * to license@woocommerce.com so we can send you a copy immediately.
12
+ *
13
+ * DISCLAIMER
14
+ *
15
+ * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16
+ * versions in the future. If you wish to customize WooCommerce Square for your
17
+ * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18
+ *
19
+ * @author WooCommerce
20
+ * @copyright Copyright: (c) 2019, Automattic, Inc.
21
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
+ */
23
+
24
+ namespace WooCommerce\Square\Utilities;
25
+
26
+ defined( 'ABSPATH' ) || exit;
27
+
28
+ /**
29
+ * Helper for dealing with String values.
30
+ *
31
+ * @since 2.2.0
32
+ */
33
+ class String_Utility {
34
+
35
+ /**
36
+ * Truncates $string after a given $length if string is longer than
37
+ * $length.
38
+ *
39
+ * The last characters will be replaced with the $omission string
40
+ * for a total length not exceeding $length
41
+ *
42
+ * See SV_WC_Helper::str_truncate()
43
+ *
44
+ * @since 2.2.0
45
+ * @param string $string text to truncate
46
+ * @param int $length total desired length of string, including omission
47
+ * @param string $omission omission text, defaults to '...'
48
+ * @return string
49
+ */
50
+ public static function truncate( $string, $length, $omission = '...' ) {
51
+ $string = self::to_ascii( $string );
52
+
53
+ // bail if string doesn't need to be truncated
54
+ if ( strlen( $string ) <= $length ) {
55
+ return $string;
56
+ }
57
+
58
+ $length -= strlen( $omission );
59
+
60
+ return substr( $string, 0, $length ) . $omission;
61
+ }
62
+
63
+ /**
64
+ * Returns a string with all non-ASCII characters removed. This is useful
65
+ * for any string functions that expect only ASCII chars and can't
66
+ * safely handle UTF-8. Note this only allows ASCII chars in the range
67
+ * 33-126 (newlines/carriage returns are stripped)
68
+ *
69
+ * See SV_WC_Helper::str_to_ascii()
70
+ *
71
+ * @since 2.2.0
72
+ * @param string $string string to make ASCII
73
+ * @return string
74
+ */
75
+ public static function to_ascii( $string ) {
76
+ // strip ASCII chars 32 and under
77
+ $string = filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW );
78
+
79
+ // strip ASCII chars 127 and higher
80
+ return filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH );
81
+ }
82
+ }
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === WooCommerce Square ===
2
- Contributors: automattic, royho, woothemes, bor0
3
  Tags: credit card, square, woocommerce, inventory sync
4
  Requires at least: 4.6
5
- Tested up to: 5.4
6
  Requires PHP: 5.6
7
- Stable tag: 2.1.6
8
  License: GPLv3
9
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -72,6 +72,26 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
72
 
73
  == Changelog ==
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  = 2.1.6 - 2020.07.15 =
76
  * Fix - Make the "Sync Now" button disabled when no business location is set in Square settings.
77
  * Fix - Enable checking/unchecking the Manage Stock setting for all variations.
1
  === WooCommerce Square ===
2
+ Contributors: woocommerce, automattic, royho, woothemes, bor0, mattdallan, menakas, chickenn00dle
3
  Tags: credit card, square, woocommerce, inventory sync
4
  Requires at least: 4.6
5
+ Tested up to: 5.5
6
  Requires PHP: 5.6
7
+ Stable tag: 2.2.0
8
  License: GPLv3
9
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
10
 
72
 
73
  == Changelog ==
74
 
75
+ = 2.2.0 - 2020.08.31 =
76
+ * Feature - Import new product variations from square to existing products in WooCommerce. PR#475
77
+ * Feature - Variations that are removed from Square will now be removed from products in WooCommerce during import. PR#475
78
+ * Feature - Upgrade to the Square Payments and Refunds API. PR#408
79
+ * Feature - New orders can be refunded up to one year after payment (up from 120 days). PR#408
80
+ * Fix - Only import products from Square that have non-empty SKUs. PR#475
81
+ * Fix - Empty product categories imported from Square into WooCommerce. PR#475
82
+ * Fix - Assign existing products to new categories imported from Square. PR#475
83
+ * Fix - Prevents loading of Square Assets on all pages except My Account -> Payment Methods & Checkout. PR#469
84
+ * Fix - Square Product Import & Product Manual Sync not triggering on mobile browsers. PR#472
85
+ * Fix - 3D Secure Verification Token is missing, Intent mismatch and other checkout errors related to SCA for merchants outside of the EU. PR#471
86
+ * Fix - Updated some of our documentation and support links in admin notices so they no longer redirect to an old URL or a 404 page. PR#474
87
+ * Fix - Use pagination to fetch inventory counts from Square. PR#478
88
+ * Fix - Display WooCommerce checkout validation errors along with Square payment form errors. PR#476
89
+ * Fix - Switching between sandbox and production environments will now show correct business locations. PR#462
90
+ * Fix - Don't wipe a customer's saved cards on when we receive an API error. PR#460
91
+ * Fix - Exclude draft and pending products from syncing from WooCommerce to Square. PR#484
92
+ * Fix - DevTools errors caused by missing minified JS files.
93
+ * Fix - PHP errors when syncing large amounts of products (`get_data() on null` and `getCursor() on a string`). PR#497
94
+
95
  = 2.1.6 - 2020.07.15 =
96
  * Fix - Make the "Sync Now" button disabled when no business location is set in Square settings.
97
  * Fix - Enable checking/unchecking the Manage Stock setting for all variations.
templates/emails/plain/square-email.php CHANGED
@@ -21,7 +21,7 @@
21
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
  */
23
 
24
- defined( 'ABSPATH' ) or exit;
25
 
26
  /**
27
  * Square plain email template.
21
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
  */
23
 
24
+ defined( 'ABSPATH' ) || exit;
25
 
26
  /**
27
  * Square plain email template.
templates/emails/square-email.php CHANGED
@@ -21,7 +21,7 @@
21
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
  */
23
 
24
- defined( 'ABSPATH' ) or exit;
25
 
26
  /**
27
  * Square html email template.
21
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
22
  */
23
 
24
+ defined( 'ABSPATH' ) || exit;
25
 
26
  /**
27
  * Square html email template.
vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/assets/js/frontend/sv-wc-payment-gateway-apple-pay.min.js.map ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": 3,
3
+ "file": "sv-wc-payment-gateway-apple-pay.min.js",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "sv-wc-payment-gateway-apple-pay.coffee"
7
+ ],
8
+ "names": [],
9
+ "mappings": "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"
10
+ }
vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/assets/js/frontend/sv-wc-payment-gateway-my-payment-methods.min.js.map ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": 3,
3
+ "file": "sv-wc-payment-gateway-my-payment-methods.min.js",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "sv-wc-payment-gateway-my-payment-methods.coffee"
7
+ ],
8
+ "names": [],
9
+ "mappings": ";AAAA;;;;;;;;;AAAA;AAAA,MAAA;;EAQA,MAAA,CAAQ,QAAR,CAAkB,CAAC,KAAnB,CAAyB,SAAC,CAAD;IACxB;WAKM,MAAM,CAAC;MAaC,uCAAE,IAAF;;;;QAEZ,IAAC,CAAA,EAAD,GAAc,IAAI,CAAC;QACnB,IAAC,CAAA,IAAD,GAAc,IAAI,CAAC;QACnB,IAAC,CAAA,IAAD,GAAc,IAAI,CAAC;QACnB,IAAC,CAAA,QAAD,GAAc,IAAI,CAAC;QACnB,IAAC,CAAA,UAAD,GAAc,IAAI,CAAC;QAGnB,IAAA,CAA8G,IAAI,CAAC,eAAnH;UAAA,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,IAAvC,CAA6C,gDAA7C,CAA+F,CAAC,IAAhG,CAAA,EAAA;;QAGA,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qCAAhB,CAAsD,CAAC,MAAvD,CAAA;QAGA,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,EAAvC,CAA2C,OAA3C,EAAoD,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,8CAAjE,EAAgH,CAAA,SAAA,KAAA;iBAAA,SAAE,KAAF;mBAAa,KAAI,CAAC,WAAL,CAAkB,KAAlB;UAAb;QAAA,CAAA,CAAA,CAAA,IAAA,CAAhH;QAGA,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,EAAvC,CAA2C,OAA3C,EAAoD,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,8CAAjE,EAAgH,CAAA,SAAA,KAAA;iBAAA,SAAE,KAAF;mBAAa,KAAI,CAAC,WAAL,CAAkB,KAAlB;UAAb;QAAA,CAAA,CAAA,CAAA,IAAA,CAAhH;QAGA,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,EAAvC,CAA2C,OAA3C,EAAoD,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qDAAjE,EAAuH,CAAA,SAAA,KAAA;iBAAA,SAAE,KAAF;mBAAa,KAAI,CAAC,WAAL,CAAkB,KAAlB;UAAb;QAAA,CAAA,CAAA,CAAA,IAAA,CAAvH;QAGA,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,EAAvC,CAA2C,OAA3C,EAAoD,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,gDAAjE,EAAkH,CAAA,SAAA,KAAA;iBAAA,SAAE,KAAF;YAEjH,IAAG,CAAA,CAAG,KAAK,CAAC,aAAT,CAAwB,CAAC,QAAzB,CAAmC,UAAnC,CAAA,IAAmD,CAAI,OAAA,CAAS,KAAC,CAAA,IAAI,CAAC,UAAf,CAA1D;qBACC,KAAK,CAAC,cAAN,CAAA,EADD;;UAFiH;QAAA,CAAA,CAAA,CAAA,IAAA,CAAlH;QAQA,CAAA,CAAG,qCAAH,CAA0C,CAAC,KAA3C,CAAiD,SAAE,KAAF;UAChD,IAA0B,CAAA,CAAG,IAAH,CAAS,CAAC,QAAV,CAAoB,UAApB,CAA1B;mBAAA,KAAK,CAAC,cAAN,CAAA,EAAA;;QADgD,CAAjD;MAhCY;;8CAyCb,WAAA,GAAa,SAAE,KAAF;AAEZ,YAAA;QAAA,KAAK,CAAC,cAAN,CAAA;QAEA,MAAA,GAAS,CAAA,CAAG,KAAK,CAAC,aAAT;QACT,GAAA,GAAS,MAAM,CAAC,OAAP,CAAgB,IAAhB;QAET,GAAG,CAAC,IAAJ,CAAU,OAAV,CAAmB,CAAC,IAApB,CAAA;QACA,GAAG,CAAC,IAAJ,CAAU,OAAV,CAAmB,CAAC,IAApB,CAAA;QACA,GAAG,CAAC,QAAJ,CAAc,SAAd;QAGA,MAAM,CAAC,IAAP,CAAa,IAAC,CAAA,IAAI,CAAC,aAAnB,CAAkC,CAAC,WAAnC,CAAgD,qBAAhD,CAAuE,CAAC,QAAxE,CAAkF,4BAAlF,CAAgH,CAAC,WAAjH,CAA8H,QAA9H;QAEA,MAAM,CAAC,QAAP,CAAiB,sBAAjB,CAAyC,CAAC,IAA1C,CAAA;QACA,MAAM,CAAC,QAAP,CAAiB,wBAAjB,CAA2C,CAAC,IAA5C,CAAA;eAEA,IAAI,CAAC,iBAAL,CAAA;MAjBY;;8CAyBb,WAAA,GAAa,SAAE,KAAF;AAEZ,YAAA;QAAA,KAAK,CAAC,cAAN,CAAA;QAEA,MAAA,GAAS,CAAA,CAAG,KAAK,CAAC,aAAT;QACT,GAAA,GAAS,MAAM,CAAC,OAAP,CAAgB,IAAhB;QAET,IAAI,CAAC,QAAL,CAAA;QAGA,GAAG,CAAC,IAAJ,CAAU,QAAV,CAAoB,CAAC,MAArB,CAAA;QAEA,IAAA,GACC;UAAA,MAAA,EAAU,KAAA,GAAM,IAAC,CAAA,EAAP,GAAU,sBAApB;UACA,KAAA,EAAU,IAAC,CAAA,UADX;UAEA,QAAA,EAAU,GAAG,CAAC,IAAJ,CAAU,UAAV,CAFV;UAGA,IAAA,EAAU,GAAG,CAAC,IAAJ,CAAU,aAAV,CAAyB,CAAC,SAA1B,CAAA,CAHV;;eAKD,CAAC,CAAC,IAAF,CAAQ,IAAC,CAAA,QAAT,EAAmB,IAAnB,CAEC,CAAC,IAFF,CAEO,CAAA,SAAA,KAAA;iBAAA,SAAE,QAAF;YAEL,IAAA,CAAuD,QAAQ,CAAC,OAAhE;AAAA,qBAAO,KAAI,CAAC,aAAL,CAAoB,GAApB,EAAyB,QAAQ,CAAC,IAAlC,EAAP;;YAGA,IAAG,QAAQ,CAAC,IAAI,CAAC,UAAjB;cACC,GAAG,CAAC,QAAJ,CAAA,CAAc,CAAC,IAAf,CAAqB,MAAA,GAAO,KAAC,CAAA,IAAR,GAAa,+BAAlC,CAAkE,CAAC,KAAnE,CAAA,CAA0E,CAAC,QAA3E,CAAqF,OAArF,CAA8F,CAAC,IAA/F,CAAqG,OAArG,CAA8G,CAAC,IAA/G,CAAqH,SAArH,EAAgI,KAAhI,EADD;;YAGA,IAAG,0BAAH;cACC,GAAG,CAAC,WAAJ,CAAiB,QAAQ,CAAC,IAAI,CAAC,IAA/B,EADD;;YAGA,IAAG,2BAAH;cACC,KAAC,CAAA,UAAD,GAAc,QAAQ,CAAC,IAAI,CAAC,MAD7B;;mBAGA,KAAI,CAAC,kBAAL,CAAA;UAdK;QAAA,CAAA,CAAA,CAAA,IAAA,CAFP,CAkBC,CAAC,IAlBF,CAkBO,CAAA,SAAA,KAAA;iBAAA,SAAE,KAAF,EAAS,UAAT,EAAqB,KAArB;mBAEL,KAAI,CAAC,aAAL,CAAoB,GAApB,EAAyB,KAAzB;UAFK;QAAA,CAAA,CAAA,CAAA,IAAA,CAlBP,CAsBC,CAAC,MAtBF,CAsBS,CAAA,SAAA,KAAA;iBAAA,SAAA;mBAEP,KAAI,CAAC,UAAL,CAAA;UAFO;QAAA,CAAA,CAAA,CAAA,IAAA,CAtBT;MAlBY;;8CAkDb,WAAA,GAAa,SAAE,KAAF;AAEZ,YAAA;QAAA,KAAK,CAAC,cAAN,CAAA;QAEA,MAAA,GAAS,CAAA,CAAG,KAAK,CAAC,aAAT;QACT,GAAA,GAAS,MAAM,CAAC,OAAP,CAAgB,IAAhB;QAET,GAAG,CAAC,IAAJ,CAAU,OAAV,CAAmB,CAAC,IAApB,CAAA;QACA,GAAG,CAAC,IAAJ,CAAU,OAAV,CAAmB,CAAC,IAApB,CAAA;QACA,GAAG,CAAC,WAAJ,CAAiB,SAAjB;QAGA,MAAM,CAAC,WAAP,CAAoB,4BAApB,CAAkD,CAAC,QAAnD,CAA6D,qBAA7D,CAAoF,CAAC,IAArF,CAA2F,IAAC,CAAA,IAAI,CAAC,WAAjG,CAA8G,CAAC,QAA/G,CAAyH,QAAzH;QAEA,MAAM,CAAC,QAAP,CAAiB,sBAAjB,CAAyC,CAAC,IAA1C,CAAA;QACA,MAAM,CAAC,QAAP,CAAiB,wBAAjB,CAA2C,CAAC,IAA5C,CAAA;eAEA,IAAI,CAAC,kBAAL,CAAA;MAjBY;;8CA0Bb,iBAAA,GAAmB,SAAA;QAGlB,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,QAAvC,CAAiD,SAAjD;eAGA,CAAA,CAAG,qCAAH,CAA0C,CAAC,QAA3C,CAAqD,UAArD;MANkB;;8CAYnB,kBAAA,GAAoB,SAAA;QAGnB,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,WAAvC,CAAoD,SAApD;eAGA,CAAA,CAAG,qCAAH,CAA0C,CAAC,WAA3C,CAAwD,UAAxD;MANmB;;8CAYpB,QAAA,GAAU,SAAA;eAAG,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,MAAvC,CAA+C,KAA/C,CAAsD,CAAC,KAAvD,CAA8D;UAAA,OAAA,EAAS,IAAT;UAAe,UAAA,EAAY;YAAA,UAAA,EAAY,MAAZ;YAAoB,OAAA,EAAS,GAA7B;WAA3B;SAA9D;MAAH;;8CAMV,UAAA,GAAY,SAAA;eAAG,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,qBAAhB,CAAsC,CAAC,MAAvC,CAA+C,KAA/C,CAAsD,CAAC,OAAvD,CAAA;MAAH;;8CAUZ,aAAA,GAAe,SAAE,GAAF,EAAO,KAAP,EAAc,OAAd;AAEd,YAAA;;UAF4B,UAAU;;QAEtC,OAAO,CAAC,KAAR,CAAe,KAAf;QAEA,IAAA,CAAkC,OAAlC;UAAA,OAAA,GAAU,IAAC,CAAA,IAAI,CAAC,WAAhB;;QAEA,OAAA,GAAU,CAAA,CAAG,MAAA,GAAO,IAAC,CAAA,IAAR,GAAa,iCAAhB,CAAkD,CAAC,IAAnD,CAAA;eAEV,CAAA,CAAG,iCAAA,GAAoC,OAApC,GAA8C,IAA9C,GAAqD,OAArD,GAA+D,YAAlE,CAAgF,CAAC,WAAjF,CAA8F,GAA9F,CAAmG,CAAC,IAApG,CAA0G,IAA1G,CAAgH,CAAC,KAAjH,CAAwH,IAAxH,CAA8H,CAAC,OAA/H,CAAwI,GAAxI;MARc;;;;;EAzMQ,CAAzB;AARA"
10
+ }
vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/assets/js/frontend/sv-wc-payment-gateway-payment-form.min.js.map ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": 3,
3
+ "file": "sv-wc-payment-gateway-payment-form.min.js",
4
+ "sourceRoot": "",
5
+ "sources": [
6
+ "sv-wc-payment-gateway-payment-form.coffee"
7
+ ],
8
+ "names": [],
9
+ "mappings": "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"
10
+ }
woocommerce-square.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /**
3
  * Plugin Name: WooCommerce Square
4
- * Version: 2.1.6
5
  * Plugin URI: https://woocommerce.com/products/square/
6
  * Description: Adds ability to sync inventory between WooCommerce and Square POS. In addition, you can also make purchases through the Square payment gateway.
7
  * Author: WooCommerce
@@ -19,10 +19,10 @@
19
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
20
  *
21
  * WC requires at least: 3.0
22
- * WC tested up to: 4.3
23
  */
24
 
25
- defined( 'ABSPATH' ) or exit;
26
 
27
  require_once( plugin_dir_path( __FILE__ ) . 'vendor/prospress/action-scheduler/action-scheduler.php' );
28
 
@@ -211,23 +211,35 @@ class WooCommerce_Square_Loader {
211
 
212
  if ( ! $this->is_wp_compatible() ) {
213
 
214
- $this->add_admin_notice( 'update_wordpress', 'error', sprintf(
215
- '%s requires WordPress version %s or higher. Please %supdate WordPress &raquo;%s',
216
- '<strong>' . self::PLUGIN_NAME . '</strong>',
217
- self::MINIMUM_WP_VERSION,
218
- '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">', '</a>'
219
- ) );
 
 
 
 
 
 
220
  }
221
 
222
  if ( ! $this->is_wc_compatible() ) {
223
 
224
- $this->add_admin_notice( 'update_woocommerce', 'error', sprintf(
225
- '%1$s requires WooCommerce version %2$s or higher. Please %3$supdate WooCommerce%4$s to the latest version, or %5$sdownload the minimum required version &raquo;%6$s',
226
- '<strong>' . self::PLUGIN_NAME . '</strong>',
227
- self::MINIMUM_WC_VERSION,
228
- '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">', '</a>',
229
- '<a href="' . esc_url( 'https://downloads.wordpress.org/plugin/woocommerce.' . self::MINIMUM_WC_VERSION . '.zip' ) . '">', '</a>'
230
- ) );
 
 
 
 
 
 
231
  }
232
  }
233
 
@@ -307,7 +319,7 @@ class WooCommerce_Square_Loader {
307
 
308
  $this->notices[ $slug ] = array(
309
  'class' => $class,
310
- 'message' => $message
311
  );
312
  }
313
 
1
  <?php
2
  /**
3
  * Plugin Name: WooCommerce Square
4
+ * Version: 2.2.0
5
  * Plugin URI: https://woocommerce.com/products/square/
6
  * Description: Adds ability to sync inventory between WooCommerce and Square POS. In addition, you can also make purchases through the Square payment gateway.
7
  * Author: WooCommerce
19
  * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
20
  *
21
  * WC requires at least: 3.0
22
+ * WC tested up to: 4.4
23
  */
24
 
25
+ defined( 'ABSPATH' ) || exit;
26
 
27
  require_once( plugin_dir_path( __FILE__ ) . 'vendor/prospress/action-scheduler/action-scheduler.php' );
28
 
211
 
212
  if ( ! $this->is_wp_compatible() ) {
213
 
214
+ $this->add_admin_notice(
215
+ 'update_wordpress',
216
+ 'error',
217
+ sprintf(
218
+ '%s requires WordPress version %s or higher. Please %supdate WordPress &raquo;%s',
219
+ '<strong>' . self::PLUGIN_NAME .
220
+ '</strong>',
221
+ self::MINIMUM_WP_VERSION,
222
+ '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">',
223
+ '</a>'
224
+ )
225
+ );
226
  }
227
 
228
  if ( ! $this->is_wc_compatible() ) {
229
 
230
+ $this->add_admin_notice(
231
+ 'update_woocommerce',
232
+ 'error',
233
+ sprintf(
234
+ '%1$s requires WooCommerce version %2$s or higher. Please %3$supdate WooCommerce%4$s to the latest version, or %5$sdownload the minimum required version &raquo;%6$s',
235
+ '<strong>' . self::PLUGIN_NAME . '</strong>',
236
+ self::MINIMUM_WC_VERSION,
237
+ '<a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">',
238
+ '</a>',
239
+ '<a href="' . esc_url( 'https://downloads.wordpress.org/plugin/woocommerce.' . self::MINIMUM_WC_VERSION . '.zip' ) . '">',
240
+ '</a>'
241
+ )
242
+ );
243
  }
244
  }
245
 
319
 
320
  $this->notices[ $slug ] = array(
321
  'class' => $class,
322
+ 'message' => $message,
323
  );
324
  }
325