'',
'export' => 'invoices',
'sample' => 'settings',
'peppol' => 'settings?tab=peppol',
);
/** Company detail fields: key => label. */
public static function fields() {
return array(
'name' => __( 'Company name', 'peppolkit' ),
'street' => __( 'Street and number', 'peppolkit' ),
'postalCode' => __( 'Postcode', 'peppolkit' ),
'city' => __( 'City', 'peppolkit' ),
'country' => __( 'Country', 'peppolkit' ),
'vatNumber' => __( 'VAT number', 'peppolkit' ),
'registrationNumber' => __( 'KvK or enterprise number', 'peppolkit' ),
'email' => __( 'Invoice e-mail (reply-to)', 'peppolkit' ),
'iban' => __( 'IBAN', 'peppolkit' ),
'invoicePrefix' => __( 'Invoice number prefix (optional)', 'peppolkit' ),
);
}
public static function init() {
add_action( 'admin_menu', array( __CLASS__, 'menu' ) );
add_action( 'admin_notices', array( __CLASS__, 'notices' ) );
add_action( 'admin_post_peppolkit_connect', array( __CLASS__, 'handle_connect' ) );
add_action( 'admin_post_peppolkit_settings', array( __CLASS__, 'handle_settings' ) );
add_action( 'admin_post_peppolkit_peppol', array( __CLASS__, 'handle_peppol' ) );
add_action( 'admin_post_peppolkit_plan', array( __CLASS__, 'handle_plan' ) );
add_action( 'admin_post_peppolkit_dashboard', array( __CLASS__, 'handle_dashboard' ) );
add_action( 'admin_post_peppolkit_requeue', array( __CLASS__, 'handle_requeue' ) );
add_action( 'admin_post_peppolkit_backfill', array( __CLASS__, 'handle_backfill' ) );
add_action( 'admin_post_peppolkit_disconnect', array( __CLASS__, 'handle_disconnect' ) );
add_filter( 'plugin_action_links_' . plugin_basename( PEPPOLKIT_FILE ), array( __CLASS__, 'action_links' ) );
}
public static function menu() {
add_menu_page( 'PeppolKit', 'PeppolKit', 'manage_woocommerce', 'peppolkit', array( __CLASS__, 'overview' ), self::menu_icon(), 56 );
add_submenu_page( 'peppolkit', __( 'PeppolKit overview', 'peppolkit' ), __( 'Overview', 'peppolkit' ), 'manage_woocommerce', 'peppolkit', array( __CLASS__, 'overview' ) );
add_submenu_page( 'peppolkit', __( 'PeppolKit settings', 'peppolkit' ), __( 'Settings', 'peppolkit' ), 'manage_woocommerce', 'peppolkit-settings', array( __CLASS__, 'page' ) );
}
/** Monochrome PeppolKit mark for the admin menu (WordPress tints it). */
private static function menu_icon() {
return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHBhdGggZmlsbD0iYmxhY2siIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1IDBoMzRhMTUgMTUgMCAwIDEgMTUgMTV2MzRhMTUgMTUgMCAwIDEtMTUgMTVIMTVBMTUgMTUgMCAwIDEgMCA0OVYxNUExNSAxNSAwIDAgMSAxNSAwek0xOSAxMmgxN2wxMSAxMXY4YTEyIDEyIDAgMCAxLTEyIDEyaC02djZhNCA0IDAgMCAxLTQgNGgtMmE0IDQgMCAwIDEtNC00VjE2YTQgNCAwIDAgMSA0LTR6TTI5IDIzdjEyaDVhNiA2IDAgMCAwIDAtMTJ6Ii8+PC9zdmc+';
}
/** Prints the page title with the PeppolKit mark. */
private static function heading( $title ) {
printf(
'
%s
',
esc_url( plugins_url( 'assets/logo.svg', PEPPOLKIT_FILE ) ),
esc_html( $title )
);
}
public static function action_links( $links ) {
array_unshift( $links, '' . esc_html__( 'Settings', 'peppolkit' ) . '' );
return $links;
}
private static function back( $args, $page = 'peppolkit-settings' ) {
wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php?page=' . $page ) ) );
exit;
}
private static function guard( $action ) {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( esc_html__( 'Not allowed.', 'peppolkit' ) );
}
check_admin_referer( $action );
}
/** One-shot admin notice for the current user, shown on the next page load. */
public static function flash( $type, $message ) {
set_transient( 'peppolkit_notice_' . get_current_user_id(), array( $type, $message ), 120 );
}
/** Small POST form with a single button. */
/** Prints a small POST form with a single button. */
private static function button( $action, $label, $class = 'button', $hidden = array() ) {
printf(
'', esc_attr( $class ), esc_html( $label ) );
}
public static function notices() {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$key = 'peppolkit_notice_' . get_current_user_id();
$flash = get_transient( $key );
if ( is_array( $flash ) ) {
delete_transient( $key );
printf( '', esc_attr( 'error' === $flash[0] ? 'error' : 'success' ), esc_html( $flash[1] ) );
}
if ( ! PeppolKit_Api::connected() || ! self::own_screen() ) {
return;
}
$pending = PeppolKit_Orders::pending_count();
if ( $pending > 0 ) {
echo '' . esc_html(
sprintf(
/* translators: %d: number of orders */
_n( '%d order is waiting for a PeppolKit invoice (invoice limit reached or company details missing).', '%d orders are waiting for a PeppolKit invoice (invoice limit reached or company details missing).', $pending, 'peppolkit' ),
$pending
)
) . '
';
self::button( 'peppolkit_requeue', __( 'Create invoices now', 'peppolkit' ), 'button button-primary' );
echo ' ' . esc_html__( 'Settings and plan', 'peppolkit' ) . '
';
}
}
/** The waiting-orders notice only shows on the PeppolKit pages and the WooCommerce order screens. */
private static function own_screen() {
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
if ( ! $screen ) {
return false;
}
return false !== strpos( $screen->id, 'peppolkit' ) || in_array( $screen->id, array( 'edit-shop_order', 'shop_order', 'woocommerce_page_wc-orders' ), true );
}
public static function handle_connect() {
self::guard( 'peppolkit_connect' );
$res = PeppolKit_Api::connect( sanitize_email( wp_unslash( $_POST['email'] ?? '' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in guard().
self::back( is_wp_error( $res ) ? array( 'pk_error' => rawurlencode( $res->get_error_message() ) ) : array( 'pk_ok' => 'connected' ) );
}
public static function handle_settings() {
self::guard( 'peppolkit_settings' );
$body = array();
foreach ( array_keys( self::fields() ) as $k ) {
$v = sanitize_text_field( wp_unslash( $_POST[ $k ] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in guard().
if ( '' !== $v ) {
$body[ $k ] = $v;
}
}
foreach ( array( 'ossRegistered', 'belowMicroThreshold', 'emailCustomers', 'peppolSend' ) as $k ) {
$body[ $k ] = ! empty( $_POST[ $k ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in guard().
}
$lang = sanitize_key( wp_unslash( $_POST['language'] ?? 'auto' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in guard().
$body['language'] = in_array( $lang, array( 'auto', 'nl', 'fr', 'en' ), true ) ? $lang : 'auto';
update_option( 'peppolkit_settings_cache', $body, false );
$res = PeppolKit_Api::request( 'PUT', '/settings', $body );
if ( is_wp_error( $res ) ) {
self::back( array( 'pk_error' => rawurlencode( $res->get_error_message() ) ) );
}
// Orders that waited for company details get their invoice now.
PeppolKit_Orders::requeue_pending();
self::back( array( 'pk_ok' => 'saved' ) );
}
public static function handle_peppol() {
self::guard( 'peppolkit_peppol' );
$res = PeppolKit_Api::request( 'POST', '/peppol' );
if ( is_wp_error( $res ) ) {
self::back( array( 'pk_error' => rawurlencode( $res->get_error_message() ) ) );
}
if ( ! empty( $res['verifyUrl'] ) ) {
wp_redirect( $res['verifyUrl'] ); // phpcs:ignore WordPress.Security.SafeRedirect -- identity check at the Peppol access point.
exit;
}
self::back( array( 'pk_ok' => 'peppol_' . sanitize_key( $res['status'] ?? '' ) ) );
}
/**
* Plan names come from the API in Dutch (they are also its keys); show them in the admin language.
*
* @param string $name Plan key: Gratis, Starter, Groei or Pro.
*/
private static function plan_label( $name ) {
$labels = array(
'Gratis' => __( 'Free', 'peppolkit' ),
'Groei' => __( 'Growth', 'peppolkit' ),
);
return $labels[ $name ] ?? $name;
}
public static function handle_plan() {
self::guard( 'peppolkit_plan' );
$plan = sanitize_text_field( wp_unslash( $_POST['plan'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in guard().
$res = PeppolKit_Api::request(
'POST',
'/billing',
array(
'plan' => $plan,
'redirectUrl' => admin_url( 'admin.php?page=peppolkit-settings&pk_ok=plan' ),
)
);
if ( is_wp_error( $res ) ) {
self::back( array( 'pk_error' => rawurlencode( $res->get_error_message() ) ) );
}
if ( ! empty( $res['checkoutUrl'] ) ) {
wp_redirect( $res['checkoutUrl'] ); // phpcs:ignore WordPress.Security.SafeRedirect -- Mollie checkout.
exit;
}
// A plan change may lift the invoice limit.
PeppolKit_Orders::requeue_pending();
self::back( array( 'pk_ok' => 'plan' ) );
}
/** One-click login to the hosted PeppolKit dashboard. */
public static function handle_dashboard() {
self::guard( 'peppolkit_dashboard' );
$target = sanitize_key( wp_unslash( $_POST['target'] ?? 'open' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in guard().
$page = self::DASHBOARD_PAGES[ $target ] ?? '';
$res = PeppolKit_Api::request( 'POST', '/dashboard-link?page=' . rawurlencode( $page ) );
if ( is_wp_error( $res ) || empty( $res['url'] ) ) {
self::back( array( 'pk_error' => rawurlencode( is_wp_error( $res ) ? $res->get_error_message() : __( 'The dashboard is not available right now.', 'peppolkit' ) ) ), 'peppolkit' );
}
wp_redirect( $res['url'] ); // phpcs:ignore WordPress.Security.SafeRedirect -- hosted PeppolKit dashboard.
exit;
}
public static function handle_requeue() {
self::guard( 'peppolkit_requeue' );
$n = PeppolKit_Orders::requeue_pending();
/* translators: %d: number of orders */
self::flash( 'success', sprintf( _n( '%d order queued for an invoice.', '%d orders queued for an invoice.', $n, 'peppolkit' ), $n ) );
self::back( array(), 'peppolkit' );
}
public static function handle_backfill() {
self::guard( 'peppolkit_backfill' );
$n = PeppolKit_Orders::backfill( 60, 250 );
/* translators: %d: number of orders */
self::flash( 'success', sprintf( _n( '%d paid order queued for an invoice.', '%d paid orders queued for an invoice.', $n, 'peppolkit' ), $n ) );
self::back( array(), 'peppolkit' );
}
public static function handle_disconnect() {
self::guard( 'peppolkit_disconnect' );
// Tell PeppolKit first: the account is removed 30 days later unless the store connects again.
PeppolKit_Api::request( 'POST', '/disconnect' );
delete_option( PeppolKit_Api::OPTION_KEY );
delete_option( 'peppolkit_settings_cache' );
if ( function_exists( 'as_unschedule_all_actions' ) ) {
as_unschedule_all_actions( '', array(), 'peppolkit' );
}
self::flash( 'success', __( 'PeppolKit is disconnected. Your invoices stay available in your PeppolKit dashboard for 30 days; download them before then. Connect again within 30 days to keep everything.', 'peppolkit' ) );
self::back( array(), 'peppolkit' );
}
private static function messages() {
if ( isset( $_GET['pk_error'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- display only.
echo '' . esc_html( rawurldecode( sanitize_text_field( wp_unslash( $_GET['pk_error'] ) ) ) ) . '
'; // phpcs:ignore WordPress.Security.NonceVerification
}
$messages = array(
'connected' => __( 'Connected to PeppolKit. Now fill in your company details.', 'peppolkit' ),
'saved' => __( 'Saved. Paid orders now get an invoice automatically.', 'peppolkit' ),
'peppol_active' => __( 'Peppol is active.', 'peppolkit' ),
'peppol_unavailable' => __( 'Peppol sending is not available yet.', 'peppolkit' ),
'plan' => __( 'Plan updated. A paid plan becomes active as soon as Mollie confirms the payment.', 'peppolkit' ),
);
$ok = sanitize_key( wp_unslash( $_GET['pk_ok'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification
if ( isset( $messages[ $ok ] ) ) {
echo '' . esc_html( $messages[ $ok ] ) . '
';
}
}
private static function connect_form() {
echo '' . esc_html__( 'PeppolKit turns every paid order into an EU-compliant invoice with a Peppol e-invoice. Connect your shop to start (free up to 10 invoices a month).', 'peppolkit' ) . '
';
echo '';
}
/** The free plan sends over Peppol through the merchant's own free Peppyrus account, not through PeppolKit's partner. */
private static function sends_via_own_peppyrus( $plan ) {
return is_array( $plan ) && 0 === (int) ( $plan['peppolIncluded'] ?? 0 );
}
private static function usage_bar( $label, $used, $limit ) {
$used = (int) $used;
echo '' . esc_html( $label ) . ' ';
if ( null === $limit ) {
/* translators: %d: count used */
echo esc_html( sprintf( __( '%d (unlimited)', 'peppolkit' ), $used ) ) . '
';
return;
}
$limit = max( 0, (int) $limit );
/* translators: 1: count used, 2: limit */
echo esc_html( sprintf( __( '%1$d of %2$d', 'peppolkit' ), $used, $limit ) ) . '';
$pct = $limit > 0 ? min( 100, (int) round( 100 * $used / $limit ) ) : 100;
$color = $pct >= 100 ? '#d63638' : ( $pct >= 80 ? '#dba617' : '#2271b1' );
printf( '', (int) $pct, esc_attr( $color ) );
}
/** Overview: setup checklist, usage, latest invoices, dashboard shortcuts. */
public static function overview() {
echo '';
self::heading( __( 'PeppolKit overview', 'peppolkit' ) );
self::messages();
if ( ! PeppolKit_Api::connected() ) {
self::connect_form();
echo '
';
return;
}
$remote = PeppolKit_Api::request( 'GET', '/settings' );
$remote = is_wp_error( $remote ) ? array() : $remote;
$list = PeppolKit_Api::request( 'GET', '/invoices' );
$invoices = is_wp_error( $list ) ? array() : array_slice( (array) ( $list['invoices'] ?? array() ), 0, 10 );
$usage = (array) ( $remote['usage'] ?? array() );
echo '';
self::button( 'peppolkit_dashboard', __( 'Open dashboard', 'peppolkit' ), 'button button-primary', array( 'target' => 'open' ) );
self::button( 'peppolkit_dashboard', __( 'Monthly export', 'peppolkit' ), 'button', array( 'target' => 'export' ) );
self::button( 'peppolkit_dashboard', __( 'Sample invoice', 'peppolkit' ), 'button', array( 'target' => 'sample' ) );
echo '
';
echo '' . esc_html__( 'Setup', 'peppolkit' ) . '
';
$steps = array(
array( ! empty( $remote['configured'] ), __( 'Company details saved', 'peppolkit' ), 'peppolkit-settings' ),
array( ! empty( $remote['emailCustomers'] ), __( 'Invoices are e-mailed to customers', 'peppolkit' ), 'peppolkit-settings' ),
array( ! empty( $invoices ) || ! empty( $usage['invoicesThisMonth'] ), __( 'First invoice created', 'peppolkit' ), '' ),
array( ! empty( $remote['peppolActive'] ), __( 'Peppol active', 'peppolkit' ), 'peppolkit-settings' ),
);
foreach ( $steps as $s ) {
printf(
'- ',
esc_attr( $s[0] ? 'dashicons-yes-alt' : 'dashicons-marker' ),
esc_attr( $s[0] ? '#00a32a' : '#8c8f94' )
);
if ( ! $s[0] && $s[2] ) {
printf( '%s', esc_url( admin_url( 'admin.php?page=' . $s[2] ) ), esc_html( $s[1] ) );
} else {
echo esc_html( $s[1] );
}
echo '
';
}
echo '
';
if ( ! empty( $remote['plan'] ) ) {
$plan = $remote['plan'];
/* translators: %s: plan name */
echo '' . esc_html( sprintf( __( 'Usage this month (plan %s)', 'peppolkit' ), self::plan_label( $plan['name'] ) ) ) . '
';
if ( $usage ) {
self::usage_bar( __( 'Invoices', 'peppolkit' ), $usage['invoicesThisMonth'] ?? 0, array_key_exists( 'invoicesPerMonth', $plan ) ? $plan['invoicesPerMonth'] : null );
if ( self::sends_via_own_peppyrus( $plan ) ) {
echo '' . esc_html__( 'Peppol', 'peppolkit' ) . ' ' . esc_html__( 'is sent through your own free Peppyrus account and is not counted here.', 'peppolkit' ) . '
';
} else {
self::usage_bar( __( 'Peppol sends', 'peppolkit' ), $usage['peppolThisMonth'] ?? 0, (int) ( $plan['peppolIncluded'] ?? 0 ) );
}
} else {
echo '' . esc_html__( 'Usage figures are not available yet.', 'peppolkit' ) . '
';
}
}
echo '' . esc_html__( 'Latest invoices', 'peppolkit' ) . '
';
if ( ! $invoices ) {
echo '' . esc_html__( 'No invoices yet. They are created as soon as an order is paid.', 'peppolkit' ) . '
';
} else {
echo '| ' . esc_html__( 'Number', 'peppolkit' ) . ' | ' . esc_html__( 'Order', 'peppolkit' ) . ' | ' . esc_html__( 'Date', 'peppolkit' ) . ' | ' . esc_html__( 'Total', 'peppolkit' ) . ' | Peppol | ' . esc_html__( 'E-mail', 'peppolkit' ) . ' | PDF |
';
foreach ( $invoices as $inv ) {
$order_id = (int) strtok( (string) ( $inv['orderId'] ?? '' ), '/' );
$order = $order_id ? wc_get_order( $order_id ) : false;
echo '| ' . esc_html( ( 'credit_note' === ( $inv['type'] ?? '' ) ? __( 'Credit note', 'peppolkit' ) . ' ' : '' ) . $inv['number'] ) . ' | ';
if ( $order ) {
printf( '#%s', esc_url( $order->get_edit_order_url() ), esc_html( $order->get_order_number() ) );
} else {
echo esc_html( (string) ( $inv['orderId'] ?? '' ) );
}
printf(
' | %s | %s | %s | %s | PDF |
',
esc_html( $inv['issueDate'] ?? '' ),
wp_kses_post( wc_price( (float) $inv['total'], array( 'currency' => $inv['currency'] ?? '' ) ) ),
esc_html( PeppolKit_Orders::peppol_label( $inv['peppolStatus'] ?? null ) ),
esc_html( PeppolKit_Orders::email_label( $inv ) ),
esc_url( $inv['pdfUrl'] )
);
}
echo '
';
echo '';
self::button( 'peppolkit_dashboard', __( 'All invoices in the dashboard', 'peppolkit' ), 'button-link', array( 'target' => 'export' ) );
echo '
';
}
echo '' . esc_html__( 'Earlier orders', 'peppolkit' ) . '
' . esc_html__( 'Paid orders from before you connected PeppolKit have no invoice yet. This queues up to 250 of them.', 'peppolkit' ) . '
';
self::button( 'peppolkit_backfill', __( 'Create invoices for paid orders of the last 60 days', 'peppolkit' ) );
echo '';
}
/**
* The hosted dashboard can edit the same settings: when the server is configured its values win,
* and the local copy is updated to match. Keys the server does not return keep the local value.
*/
private static function sync_from_server( $remote ) {
$cache = (array) get_option( 'peppolkit_settings_cache', array() );
if ( is_wp_error( $remote ) || empty( $remote['configured'] ) ) {
return $cache;
}
$seller = (array) ( $remote['seller'] ?? array() );
$addr = (array) ( $seller['address'] ?? array() );
$server = array(
'name' => $seller['name'] ?? null,
'street' => $addr['street'] ?? null,
'postalCode' => $addr['postalCode'] ?? null,
'city' => $addr['city'] ?? null,
'country' => $addr['country'] ?? null,
'vatNumber' => $seller['vatNumber'] ?? null,
'registrationNumber' => $seller['registrationNumber'] ?? null,
'email' => $seller['email'] ?? null,
'iban' => $remote['iban'] ?? ( $seller['iban'] ?? null ),
'invoicePrefix' => $remote['invoicePrefix'] ?? ( $seller['invoicePrefix'] ?? null ),
'language' => $remote['language'] ?? null,
);
foreach ( array( 'ossRegistered', 'belowMicroThreshold', 'emailCustomers', 'peppolSend' ) as $k ) {
$server[ $k ] = isset( $remote[ $k ] ) ? (bool) $remote[ $k ] : null;
}
$merged = array_merge( $cache, array_filter( $server, function ( $v ) { return null !== $v; } ) );
if ( $merged !== $cache ) {
update_option( 'peppolkit_settings_cache', $merged, false );
}
return $merged;
}
private static function plans_section() {
$b = PeppolKit_Api::request( 'GET', '/billing' );
if ( is_wp_error( $b ) || empty( $b['available'] ) ) {
return;
}
echo '' . esc_html__( 'Plan', 'peppolkit' ) . '
' . esc_html( sprintf( /* translators: %s: plan */ __( 'Current plan: %s', 'peppolkit' ), $b['current'] ) ) . '
';
foreach ( $b['plans'] as $p ) {
echo '| ' . esc_html( self::plan_label( $p['name'] ) ) . ' | ' . esc_html( null === $p['invoicesPerMonth'] ? __( 'Unlimited invoices', 'peppolkit' ) : sprintf( /* translators: %d: invoices */ __( '%d invoices / month', 'peppolkit' ), $p['invoicesPerMonth'] ) ) . ' | ';
echo '' . esc_html( (int) $p['peppolIncluded'] > 0 ? sprintf( /* translators: %d: sends */ __( '%d Peppol sends', 'peppolkit' ), $p['peppolIncluded'] ) : __( 'Peppol via your own Peppyrus account', 'peppolkit' ) ) . ' | € ' . esc_html( number_format_i18n( $p['gross'] / 100, 2 ) ) . ( $p['vat'] ? ' ' . esc_html__( 'incl. VAT', 'peppolkit' ) : '' ) . ' / ' . esc_html__( 'month', 'peppolkit' ) . ' | ';
if ( $p['name'] !== $b['current'] ) {
self::button( 'peppolkit_plan', __( 'Choose', 'peppolkit' ), 'button', array( 'plan' => $p['name'] ) );
}
echo ' |
';
}
echo '
';
if ( 'Gratis' !== $b['current'] ) {
echo '';
self::button( 'peppolkit_plan', __( 'Back to the free plan', 'peppolkit' ), 'button-link', array( 'plan' => 'Gratis' ) );
echo '
';
}
}
public static function page() {
echo '';
self::heading( __( 'PeppolKit settings', 'peppolkit' ) );
self::messages();
if ( ! PeppolKit_Api::connected() ) {
self::connect_form();
echo '
';
return;
}
$remote = PeppolKit_Api::request( 'GET', '/settings' );
$cache = self::sync_from_server( $remote );
$val = function ( $k ) use ( $cache ) {
return (string) ( $cache[ $k ] ?? '' );
};
$flag = function ( $k, $default ) use ( $cache ) {
return isset( $cache[ $k ] ) ? (bool) $cache[ $k ] : $default;
};
if ( ! is_wp_error( $remote ) && ! empty( $remote['plan'] ) ) {
if ( self::sends_via_own_peppyrus( $remote['plan'] ) ) {
printf(
'%s
',
esc_html(
sprintf(
/* translators: %s: plan name */
__( 'Plan: %s · Peppol goes through your own free Peppyrus account.', 'peppolkit' ),
self::plan_label( $remote['plan']['name'] )
)
)
);
} else {
printf(
'%s
',
esc_html(
sprintf(
/* translators: 1: plan name, 2: Peppol sends included */
__( 'Plan: %1$s · %2$d Peppol sends a month included.', 'peppolkit' ),
self::plan_label( $remote['plan']['name'] ),
(int) $remote['plan']['peppolIncluded']
)
)
);
}
}
self::plans_section();
echo '' . esc_html__( 'Company details', 'peppolkit' ) . '
';
echo '';
echo '' . esc_html__( 'Invoice design', 'peppolkit' ) . '
' . esc_html__( 'Choose one of six designs, add your logo and pick your brand colour in the PeppolKit dashboard. They apply to every invoice.', 'peppolkit' ) . '
';
self::button( 'peppolkit_dashboard', __( 'Customise invoice design', 'peppolkit' ), 'button', array( 'target' => 'sample' ) );
echo 'Peppol
';
$via_peppyrus = ! is_wp_error( $remote ) && 'peppyrus' === ( $remote['peppolVia'] ?? '' );
$active = ! is_wp_error( $remote ) && ! empty( $remote['peppolActive'] );
if ( ! is_wp_error( $remote ) && ! empty( $remote['plan'] ) && self::sends_via_own_peppyrus( $remote['plan'] ) ) {
if ( ! ( $via_peppyrus && $active ) ) {
echo '' . esc_html__( 'Belgian businesses must receive invoices over Peppol. On the free plan you send them through your own free Peppyrus account: create the account at Peppyrus, then paste its API key in the PeppolKit dashboard. Invoices to consumers never go over Peppol.', 'peppolkit' ) . '
';
}
if ( $via_peppyrus && $active ) {
echo '' . esc_html( sprintf( /* translators: %s: Peppol participant ID */ __( 'Connected: sending over Peppol through your Peppyrus account (%s).', 'peppolkit' ), $remote['peppyrusParticipant'] ?? '' ) ) . '
';
} else {
echo '';
self::button( 'peppolkit_dashboard', __( 'Connect Peppyrus in the dashboard', 'peppolkit' ), 'button button-primary', array( 'target' => 'peppol' ) );
echo ' ' . esc_html__( 'Step-by-step guide', 'peppolkit' ) . '
';
}
} else {
echo '' . esc_html__( 'Belgian businesses must receive invoices over Peppol. Activate once; an authorised person confirms your company\'s identity at our Peppol partner.', 'peppolkit' ) . '
';
if ( $via_peppyrus && $active ) {
echo '' . esc_html( sprintf( /* translators: %s: Peppol participant ID */ __( 'Connected: sending over Peppol through your Peppyrus account (%s).', 'peppolkit' ), $remote['peppyrusParticipant'] ?? '' ) ) . '
';
} else {
if ( $active ) {
echo '' . esc_html__( 'Peppol registration started.', 'peppolkit' ) . '
';
}
echo '';
}
}
echo '' . esc_html__( 'Disconnect', 'peppolkit' ) . '
' . esc_html__( 'Removes the API key from this shop. New orders no longer get an invoice; existing invoices stay in your PeppolKit account.', 'peppolkit' ) . '
';
echo '';
}
}