package CF2022::Controller::Orders; use Mojo::Base 'Mojolicious::Controller'; use Mojo::UserAgent; use Mojo::Asset::File; use Image::PNG::QRCode 'qrpng'; use constant TPL_VS => '%d%07d'; use constant TPL_PAYMENT => 'SPD*1.0*ACC:%s*AM:%f*MSG:%s, %s*X-ID:%s*X-VS:%d*X-SS:%d*X-KS:%d'; use feature 'signatures'; no warnings qw{ experimental::signatures }; sub create ($c ) { $c->openapi->valid_input or return; my $args = $c->req->json; my $claims; my $group; my @products = (); my $accredited = 0; my $accreditation = 0; # clasifikator typu my $pt = {}; CLASS: foreach my $class ( qw(main nonfree subsidy) ) { foreach my $id ( split /\D+/, $c->config->{"products_$class"} ) { $pt->{$class}{$id} = 1; } } my $url = sprintf ('%s/organizers/%s/events/%s/orders/', $c->config->{pretix_api}, $c->config->{pretix_organizer}, $c->config->{pretix_event}, ); my @answers = map {{ question => $_->{question_id}, answer => $_->{response}, }} @{ $args->{responses} }; # processing tokenu if ( $args->{token} ) { $c->oauth_token($args->{token}); $claims = $c->oauth_claims; return $c->error(403, 'Invalid token') if ! $claims; $group = $c->oauth_main_group; return $c->error(403, 'Invalid token') if ! $group; $args->{name} = $claims->{name}; push @answers, ( { question => $c->config->{pretix_qid_sso}, answer => $claims->{sub}, }, { question => $c->config->{pretix_qid_region}, answer => $group->{region}, }, ); $accreditation = $c->config->{'products_' . $group->{role}}; if ( $group->{role} eq 'member' ) { $args->{email} = $claims->{preferred_username} .'@'. $c->config->{mail_domain}; } else { $args->{email} = $claims->{email}; } } # kontrola duplicity my $exists = $c->schema->resultset('Order')->search({ deleted => undef, email => $args->{email}, api => $url, })->count; $exists ||= $c->schema->resultset('Order')->search({ deleted => undef, sso_uuid => $claims->{sub}, api => $url, })->count if $claims; return $c->error(400, 'Duplicity' ) if $exists && ! $c->cfg->{pretix_testmode}; # korekce akreditace PRODUCT: foreach my $product ( @{ $args->{products} } ) { $product->{id} = $c->mapped_product_id($product->{id}); next PRODUCT if $pt->{subsidy}{$product->{id}} && ! $product->{price}; if ( $pt->{main}{ $product->{id} } ) { next PRODUCT if $accredited++; # neumoznit dvoji akreditace if ( $accreditation ) { $product->{id} = $accreditation; } elsif ( $pt->{nonfree}{ $product->{id} } ) { $product->{id} = $c->config->{products_fallback}; } } push @products, $product; } # fallback akreditace @products = ( { id => $c->config->{products_fallback} }, @products ) if ! $accredited; my $order = { email => $args->{email}, locale => 'en', payment_provider => 'manual', testmode => $c->config->{pretix_testmode} ? 'true' : 'false', positions => [], fees => [], }; # nahrani fotky if ( $group->{role} eq 'member' ) { my $photo_id = $c->_upload_photo($claims->{preferred_username}); if ( $photo_id ) { push @answers, ( { question => $c->config->{pretix_qid_photo} , answer => $photo_id, }, ); } } PRODUCT: foreach my $product ( @products ) { my $item = { item => $product->{id}, variation => $c->mapped_variation_id($product->{variation}), attendee_name => $args->{name}, price => $product->{price}, company => $args->{company}, }; $item->{answers} = \@answers if $pt->{main}{$product->{id}}; push @{ $order->{positions} }, $item; } $c->trace($order); # odeslani do pretixu my $ua = Mojo::UserAgent->new; my $rc = $ua->post( $url, { Authorization => 'Token ' . $c->config->{pretix_token} }, json => $order )->result; return $c->error(400, $rc->body) if ! $rc->is_success; # ukladani do lokalni databazi $order = $c->schema->resultset('Order')->create({ ip => $c->tx->remote_address, sso_uuid => $claims->{sub}, email => $order->{email}, api => $url, request => $order, response => $rc->json, order_id => $rc->json->{code}, }); $c->render( status => 201, json => { %{ $rc->json }, payment_request => { account => $c->cfg->{payment_account}, amount => $order->response->{payments}[0]{amount}, vs => sprintf(TPL_VS, $c->cfg->{payment_vs_prefix}, $order->id ), ss => $c->cfg->{payment_ss}, ks => $c->cfg->{payment_ks}, qr => '/api/orders/' . $order->id . '/payment.png', }, } ); } sub exists ($c ) { $c->openapi->valid_input or return; my $key = $c->param('key'); my $field = ( $key =~ /\@/ ) ? 'email' : 'sso_uuid'; my $exists = $c->schema->resultset('Order')->search({ deleted => undef, $field => $key, })->count; $c->render( status => 200, openapi => { exists => $exists }, ); } sub get ($c ) { $c->openapi->valid_input or return; $c->oauth_token($c->tx->req->content->headers->header('X-Token')); my $claims = $c->oauth_claims; return $c->error(403, 'Invalid token') if ! $claims; my $order = $c->schema->resultset('Order')->search({ deleted => undef, sso_uuid => $claims->{sub}, })->first; return $c->error(404, 'NOT FOUND') if ! $order; # data z pretixu (kvuli stavu, jinak je to v databazi???) $c->app->log->error($order->api . $order->order_id); my $ua = Mojo::UserAgent->new; my $rc = $ua->get( $order->api . $order->order_id . '/', { Authorization => 'Token ' . $c->config->{pretix_token} }, )->result; return $c->error(400, $rc->body) if ! $rc->is_success; $c->render( status => 200, openapi => { order => $rc->json }, ); } sub qr ($c) { my $order = $c->schema->resultset('Order')->find({ id => $c->stash->{id}, deleted => undef, }); return $c->error(404, 'NOT FOUND') if ! $order; my $payment = sprintf(TPL_PAYMENT, $c->cfg->{payment_iban}, $order->response->{payments}[0]{amount}, $c->cfg->{payment_msg}, $order->request->{positions}[0]{attendee_name}, $order->order_id, sprintf(TPL_VS, $c->cfg->{payment_vs_prefix}, $order->id ), $c->cfg->{payment_ss}, $c->cfg->{payment_ks}, ); my $png = qrpng (text => $payment, level => 4); $c->res->headers->content_type('image/png'); $c->render( data => $png ); } sub _upload_photo ($c, $username ) { my $ua = Mojo::UserAgent->new; my $rc = $ua->get( $c->config->{piratar_url} . $username . '.jpg' )->result; return if ! $rc->is_success; my $photo = "/tmp/$username.jpg"; $rc->save_to($photo); my $tx = $ua->build_tx(POST => $c->config->{pretix_api} . '/upload', { Authorization => 'Token ' . $c->config->{pretix_token}, 'Content-type' => 'image/jpeg', 'Content-Disposition' => qq{attachment; filename="$username.jpg"}, }); $tx->req->content->asset(Mojo::Asset::File->new(path => $photo)); $rc = $ua->start($tx)->result; return if ! $rc->is_success; return $rc->json->{id}; } 1;